Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MattT/APPEALS-30222 #19475

Merged
merged 29 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
877068e
Update rubocop config
ThorntonMatthew Sep 14, 2023
5cbb10c
WIP: Adding services for webex
ThorntonMatthew Sep 14, 2023
6f6ae15
Merge branch 'feature/APPEALS-26734' into MattT/APPEALS-30222
ThorntonMatthew Sep 16, 2023
2ae0933
IC updates
ThorntonMatthew Sep 21, 2023
e0b3865
Errors for create route
ThorntonMatthew Sep 21, 2023
aeb5fdd
Extract error_message
ThorntonMatthew Sep 21, 2023
87035d6
Add Meetings API impl
ThorntonMatthew Sep 22, 2023
c680bfc
Remove body from legit service
ThorntonMatthew Sep 22, 2023
68f1cb7
Remove error method
ThorntonMatthew Sep 22, 2023
2827c3f
Fix response class
ThorntonMatthew Sep 22, 2023
f5cf6a8
Update create response
ThorntonMatthew Sep 22, 2023
9077a8b
Update webex_service
ThorntonMatthew Sep 22, 2023
84d31e3
Fix typo
ThorntonMatthew Sep 22, 2023
6421656
Typo fix
ThorntonMatthew Sep 22, 2023
66c6668
Fix another typo
ThorntonMatthew Sep 22, 2023
ed6eb37
Fix constant name
ThorntonMatthew Sep 22, 2023
c93bd8d
Fix data
ThorntonMatthew Sep 22, 2023
8233e5d
Remove IC
ThorntonMatthew Sep 23, 2023
d7feae0
Add support for both virtual hearings and hearing days
ThorntonMatthew Sep 23, 2023
cd5a43c
Lint fix
ThorntonMatthew Sep 23, 2023
8f636e3
Extract out conference hash creation
ThorntonMatthew Sep 23, 2023
684b21c
Remove method
ThorntonMatthew Sep 23, 2023
9b84f35
Some minor fixes
ThorntonMatthew Sep 23, 2023
9fd0d35
Add delete reponse
ThorntonMatthew Sep 23, 2023
c4913da
Tweak method signature
ThorntonMatthew Sep 23, 2023
499a808
Update pexip tests
ThorntonMatthew Sep 23, 2023
dc0a329
Merge branch 'feature/APPEALS-26734' into MattT/APPEALS-30222
ThorntonMatthew Sep 23, 2023
b5e2fd3
More specs
ThorntonMatthew Sep 23, 2023
feb5e6d
Merge branch 'MattT/APPEALS-30222' of https://github.com/department-o…
ThorntonMatthew Sep 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ AllCops:
- 'client/node_modules/**/*'
- 'app/mappers/zip_code_to_lat_lng_mapper.rb'
- 'db/seeds/*'
TargetRailsVersion: 5.1
TargetRubyVersion: 2.5
TargetRailsVersion: 5.2.8.2
TargetRubyVersion: 2.7.3
UseCache: true

Bundler/OrderedGems:
Expand Down
6 changes: 1 addition & 5 deletions app/jobs/virtual_hearings/create_conference_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,7 @@ def error_display(response)
end

def create_new_conference
client.create_conference(
host_pin: virtual_hearing.host_pin,
guest_pin: virtual_hearing.guest_pin,
name: virtual_hearing.alias
)
client.create_conference(virtual_hearing)
end

def should_initialize_alias_and_pins?
Expand Down
9 changes: 9 additions & 0 deletions app/models/hearing_day.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,15 @@ def conference_link
@conference_link ||= find_or_create_conference_link!
end

def meeting_details_for_conference
{
title: "Guest Link for #{scheduled_for.strftime("%b %e, %Y")}",
start: scheduled_for.beginning_of_day.iso8601,
end: scheduled_for.end_of_day.iso8601,
timezone: "America/New_York"
}
end

private

def assign_created_by_user
Expand Down
11 changes: 11 additions & 0 deletions app/models/hearings/virtual_hearing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,17 @@ def rebuild_and_save_links
update!(host_hearing_link: link_service.host_link, guest_hearing_link: link_service.guest_link)
end

def meeting_details_for_conference
appeal = hearing.appeal

{
title: "#{appeal.docket_number}_#{appeal.id}_#{appeal.class}",
start: hearing.scheduled_for.beginning_of_day.iso8601,
end: hearing.scheduled_for.end_of_day.iso8601,
timezone: hearing.scheduled_for.time_zone.name
}
end

private

def assign_created_by_user
Expand Down
6 changes: 5 additions & 1 deletion app/services/external_api/pexip_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ def initialize(host:, port: 443, user_name:, password:, client_host:)
@client_host = client_host
end

def create_conference(host_pin:, guest_pin:, name:)
def create_conference(virtual_hearing)
host_pin = virtual_hearing.host_pin
guest_pin = virtual_hearing.guest_pin
name = virtual_hearing.alias

body = {
"aliases": [{ "alias": "BVA#{name}" }, { "alias": VirtualHearing.formatted_alias(name) }, { "alias": name }],
"allow_guests": true,
Expand Down
11 changes: 11 additions & 0 deletions app/services/external_api/webex_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class ExternalApi::WebexService
def create_conference(*)
fail NotImplementedError
end

def delete_conference(*)
fail NotImplementedError
end
end
7 changes: 7 additions & 0 deletions app/services/external_api/webex_service/create_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class ExternalApi::WebexService::CreateResponse < ExternalApi::WebexService::Response
def data
resp.raw_body
end
end
3 changes: 3 additions & 0 deletions app/services/external_api/webex_service/delete_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

class ExternalApi::WebexService::DeleteResponse < ExternalApi::WebexService::Response; end
56 changes: 56 additions & 0 deletions app/services/external_api/webex_service/response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

class ExternalApi::WebexService::Response
attr_reader :resp, :code

DEFAULT_ERROR_BODY = {
message: "Either an error message was not provided or one could not be located.",
descriptions: []
}.freeze

def initialize(resp)
@resp = resp
@code = @resp.code
end

def data
fail NotImplementedError
end

def error
check_for_errors
end

def success?
!resp.error?
end

private

def check_for_errors
return if success?

parsed_messages = parse_error_message

Caseflow::Error::WebexApiError.new(
code: code,
message: parsed_messages.dig(:message),
descriptions: parsed_messages.dig(:descriptions)
)
end

def parse_error_message
return DEFAULT_ERROR_BODY if resp.raw_body.empty?

begin
body = JSON.parse(resp.raw_body)

{
message: body.dig(:message),
descriptions: body.dig(:errors)&.pluck(:description)&.compact
}
rescue JSON::ParserError
DEFAULT_ERROR_BODY
end
end
end
1 change: 1 addition & 0 deletions config/initializers/webex.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WebexService = (ApplicationController.dependencies_faked? ? Fakes::WebexService : ExternalApi::WebexService)
4 changes: 3 additions & 1 deletion lib/caseflow/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,9 @@ class PexipNotFoundError < PexipApiError; end
class PexipBadRequestError < PexipApiError; end
class PexipMethodNotAllowedError < PexipApiError; end

class WebexApiError < ConferenceCreationError; end
class WebexApiError < ConferenceCreationError
attr_accessor :descriptions
end

class WorkModeCouldNotUpdateError < StandardError; end

Expand Down
158 changes: 158 additions & 0 deletions lib/fakes/webex_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# frozen_string_literal: true

class Fakes::WebexService
DEFAULT_MEETING_PROPERTIES = {
hostUserId: Faker::Alphanumeric.alphanumeric(number: 79),
hostDisplayName: "BVA Caseflow",
hostEmail: "testaccount@admindomain.com",
hostKey: "123456",
siteUrl: "test.webex.com",
webLink: "https://test.webex.com/not-real/j.php?MTID=m#{Faker::Alphanumeric.alphanumeric(number: 32).downcase}",
sipAddress: "12345678910@test.webex.com",
dialInIpAddress: "",
enabledAutoRecordMeeting: false,
allowAnyUserToBeCoHost: false,
allowFirstUserToBeCoHost: false,
allowAuthenticatedDevices: true,
enabledJoinBeforeHost: false,
joinBeforeHostMinutes: 0,
enableConnectAudioBeforeHost: false,
excludePassword: false,
publicMeeting: false,
enableAutomaticLock: false,
meetingType: "meetingSeries",
state: "active",
unlockedMeetingJoinSecurity: "allowJoinWithLobby",
meetingOptions: {
enabledChat: true,
enabledVideo: true,
enabledNote: true,
noteType: "allowAll",
enabledFileTransfer: true,
enabledUCFRichMedia: true
},
attendeePrivileges: {
enabledShareContent: true,
enabledSaveDocument: false,
enabledPrintDocument: false,
enabledAnnotate: false,
enabledViewParticipantList: true,
enabledViewThumbnails: false,
enabledRemoteControl: true,
enabledViewAnyDocument: false,
enabledViewAnyPage: false,
enabledContactOperatorPrivately: false,
enabledChatHost: true,
enabledChatPresenter: true,
enabledChatOtherParticipants: true
},
sessionTypeId: 3,
scheduledType: "meeting",
simultaneousInterpretation: {
enabled: false
},
enabledBreakoutSessions: false,
audioConnectionOptions: {
audioConnectionType: "webexAudio",
enabledTollFreeCallIn: false,
enabledGlobalCallIn: true,
enabledAudienceCallBack: false,
entryAndExitTone: "beep",
allowHostToUnmuteParticipants: false,
allowAttendeeToUnmuteSelf: true,
muteAttendeeUponEntry: true
}
}.freeze

def initialize(**args)
@status_code = args[:status_code]
@error_message = args[:error_message] || "Error"
end

def create_conference(conferenced_item)
if error?
return ExternalApi::WebexService::CreateResponse.new(
HTTPI::Response.new(
@status_code,
{},
error_response
)
)
end

ExternalApi::WebexService::CreateResponse.new(
HTTPI::Response.new(
200,
{},
generate_meetings_api_conference(conferenced_item)
)
)
end

def delete_conference(conference_id:)
if error?
return ExternalApi::WebexService::DeleteResponse.new(
HTTPI::Response.new(
@status_code, {}, error_response
)
)
end

ExternalApi::WebexService::DeleteResponse.new(HTTPI::Response.new(204, {}, {}))
end

private

def error?
[
400, 401, 403, 404, 405, 409, 410,
500, 502, 503, 504
].include? @status_code
end

def error_response
{
message: @error_message,
errors: [
description: @error_message
],
trackingId: "ROUTER_#{SecureRandom.uuid}"
}
end

def telephony_options(conf_id, meeting_num)
{
telephony: {
accessCode: meeting_num,
callInNumbers: [
{
label: "United States Toll",
callInNumber: Faker::PhoneNumber.phone_number,
tollType: "toll"
}
],
links: [
{
rel: "globalCallinNumbers",
href: "/v1/meetings/#{conf_id}/globalCallinNumbers",
method: "GET"
}
]
}
}
end

def generate_meetings_api_conference(conferenced_item)
conf_id = Faker::Alphanumeric.alphanumeric(number: 32).downcase
meeting_num = Faker::Number.number(digits: 11)

{
id: conf_id,
meetingNumber: meeting_num,
password: Faker::Alphanumeric.alphanumeric(number: 11, min_alpha: 3, min_numeric: 3),
phoneAndVideoSystemPassword: Faker::Number.number(digits: 8)
}.merge(telephony_options(conf_id, meeting_num))
.merge(DEFAULT_MEETING_PROPERTIES)
.merge(conferenced_item.meeting_details_for_conference)
end
end
22 changes: 22 additions & 0 deletions spec/models/hearing_day_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -571,4 +571,26 @@ def format_begins_at_from_db(time_string, scheduled_for)
expect(subject.hearing_day_id).to eq(hearing_day.id)
end
end

context "#meeting_details_for_conference" do
let(:expected_date) { "Sep 21, 2023" }
let(:expected_date_parsed) { Date.parse(expected_date) }
let(:hearing_day) do
build(
:hearing_day,
scheduled_for: expected_date_parsed
)
end

subject { hearing_day.meeting_details_for_conference }

it "returns the expected meeting conference details" do
is_expected.to eq(
title: "Guest Link for #{expected_date}",
start: expected_date_parsed.beginning_of_day.iso8601,
end: expected_date_parsed.end_of_day.iso8601,
timezone: "America/New_York"
)
end
end
end
37 changes: 37 additions & 0 deletions spec/models/hearings/virtual_hearing_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -474,4 +474,41 @@ def link(name)
end
end
end

context "#meeting_details_for_conference" do
let(:expected_date) { "Sep 22, 2023" }
let(:expected_date_parsed) { Date.parse(expected_date) }
let(:hearing_day) do
build(:hearing_day, scheduled_for: expected_date_parsed)
end
let(:virtual_hearing) { create(:virtual_hearing, hearing: hearing) }

subject { virtual_hearing.meeting_details_for_conference }

context "For an AMA Hearing" do
let(:hearing) { create(:hearing, hearing_day: hearing_day) }

it "returns the expected meeting conference details" do
is_expected.to eq(
title: "#{hearing.appeal.docket_number}_#{hearing.appeal.id}_Appeal",
start: "2023-09-22T00:00:00-04:00",
end: "2023-09-22T23:59:59-04:00",
timezone: "America/New_York"
)
end
end

context "For a Legacy Hearing" do
let(:hearing) { create(:legacy_hearing, hearing_day: hearing_day) }

it "returns the expected meeting conference details" do
is_expected.to eq(
title: "#{hearing.appeal.docket_number}_#{hearing.appeal.id}_LegacyAppeal",
start: "2023-09-22T00:00:00-04:00",
end: "2023-09-22T23:59:59-04:00",
timezone: "America/New_York"
)
end
end
end
end
Loading
Loading