diff --git a/app/controllers/v0/disability_compensation_forms_controller.rb b/app/controllers/v0/disability_compensation_forms_controller.rb index 05715282ffb..eee5dc0b064 100644 --- a/app/controllers/v0/disability_compensation_forms_controller.rb +++ b/app/controllers/v0/disability_compensation_forms_controller.rb @@ -35,9 +35,10 @@ def separation_locations :all_users, :get_separation_locations ) do + provider = Flipper.enabled?(:disability_compensation_staging_lighthouse_brd) ? :lighthouse_staging : nil api_provider = ApiProviderFactory.call( type: ApiProviderFactory::FACTORIES[:brd], - provider: nil, + provider:, options: {}, current_user: @current_user, feature_toggle: ApiProviderFactory::FEATURE_TOGGLE_BRD diff --git a/config/features.yml b/config/features.yml index c8cb0cb29c6..b221c1aeefb 100644 --- a/config/features.yml +++ b/config/features.yml @@ -87,6 +87,9 @@ features: caregiver_retry_form_validation: actor_type: user description: Enables 1010CG to retry schema validation + disability_compensation_staging_lighthouse_brd: + actor_type: user + description: Switches to Lighthouse Staging BRD Service. NEVER ENABLE IN PRODUCTION. hca_browser_monitoring_enabled: actor_type: user description: Enables browser monitoring for the health care application. diff --git a/lib/disability_compensation/factories/api_provider_factory.rb b/lib/disability_compensation/factories/api_provider_factory.rb index a5da3ee093c..d100bac04fd 100644 --- a/lib/disability_compensation/factories/api_provider_factory.rb +++ b/lib/disability_compensation/factories/api_provider_factory.rb @@ -15,6 +15,7 @@ require 'disability_compensation/providers/brd/brd_provider' require 'disability_compensation/providers/brd/evss_brd_provider' require 'disability_compensation/providers/brd/lighthouse_brd_provider' +require 'disability_compensation/providers/brd/lighthouse_staging_brd_provider' require 'disability_compensation/providers/generate_pdf/generate_pdf_provider' require 'disability_compensation/providers/generate_pdf/evss_generate_pdf_provider' require 'disability_compensation/providers/generate_pdf/lighthouse_generate_pdf_provider' @@ -28,7 +29,8 @@ class UndefinedFactoryTypeError < StandardError; end API_PROVIDER = { evss: :evss, - lighthouse: :lighthouse + lighthouse: :lighthouse, + lighthouse_staging: :lighthouse_staging }.freeze FACTORIES = { @@ -166,6 +168,8 @@ def brd_service_provider EvssBRDProvider.new(@current_user) when API_PROVIDER[:lighthouse] LighthouseBRDProvider.new(@current_user) + when API_PROVIDER[:lighthouse_staging] + LighthouseStagingBRDProvider.new(@current_user) else raise NotImplementedError, 'No known BRD Api Provider type provided' end diff --git a/lib/disability_compensation/providers/brd/lighthouse_staging_brd_provider.rb b/lib/disability_compensation/providers/brd/lighthouse_staging_brd_provider.rb new file mode 100644 index 00000000000..a8fb0139e89 --- /dev/null +++ b/lib/disability_compensation/providers/brd/lighthouse_staging_brd_provider.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'disability_compensation/providers/brd/lighthouse_brd_provider' +require 'lighthouse/benefits_reference_data_staging/service' + +class LighthouseStagingBRDProvider < LighthouseBRDProvider + def initialize(_current_user) + super + @service = BenefitsReferenceData::Staging::Service.new + end +end diff --git a/lib/lighthouse/benefits_reference_data_staging/configuration.rb b/lib/lighthouse/benefits_reference_data_staging/configuration.rb new file mode 100644 index 00000000000..6696a31879a --- /dev/null +++ b/lib/lighthouse/benefits_reference_data_staging/configuration.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'common/client/configuration/rest' +require 'faraday/multipart' + +module BenefitsReferenceData + ## + # HTTP client configuration for the {BenefitsReferenceData::Service}, + # sets the base path, the base request headers, and a service name for breakers and metrics. + + module Staging + class Configuration < Common::Client::Configuration::REST + self.read_timeout = Settings.lighthouse.benefits_reference_data.timeout || 20 + + ## + # @return [String] Base path for benefits_reference_data URLs. + # + def base_path + settings = Settings.lighthouse.benefits_reference_data + url = settings.staging_url + path = settings.path + version = settings.version + safe_slash_merge(url, path, version) + end + + ## + # @return [String] Service name to use in breakers and metrics. + # + def service_name + 'BenefitsReferenceDataStaging' + end + + ## + # @return [Hash] The basic headers required for any benefits_reference_data API call. + # + def self.base_request_headers + key = Settings.lighthouse.staging_api_key + message = "No api_key set for LH benefits_reference_data_staging. Please set 'lighthouse.staging_api_key'" + raise message if key.nil? + + super.merge('apiKey' => key) + end + + ## + # Creates the a connection with parsing json and adding breakers functionality. + # + # @return [Faraday::Connection] a Faraday connection instance. + # + def connection + @conn ||= Faraday.new(base_path, headers: base_request_headers, request: request_options) do |faraday| + faraday.use :breakers + faraday.use Faraday::Response::RaiseError + + faraday.request :multipart + faraday.request :json + faraday.response :json + faraday.adapter Faraday.default_adapter + end + end + + private + + def safe_slash_merge(*url_segments) + url_segments.map { |segment| segment.sub(%r{^/}, '').chomp('/') }.join('/') + end + end + end +end diff --git a/lib/lighthouse/benefits_reference_data_staging/service.rb b/lib/lighthouse/benefits_reference_data_staging/service.rb new file mode 100644 index 00000000000..ada49d3bc76 --- /dev/null +++ b/lib/lighthouse/benefits_reference_data_staging/service.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'common/client/base' +require 'common/client/concerns/monitoring' +require 'common/client/errors' +require 'common/exceptions/forbidden' +require 'common/exceptions/schema_validation_errors' +require 'lighthouse/benefits_reference_data_staging/configuration' +require 'lighthouse/benefits_reference_data/service_exception' + +module BenefitsReferenceData + ## + # Proxy Service for the Lighthouse Benefits Reference Data API. + + module Staging + class Service < Common::Client::Base + include SentryLogging + include Common::Client::Concerns::Monitoring + + configuration BenefitsReferenceData::Staging::Configuration + + # ap @configuration.base_request_headers; exit + + STATSD_KEY_PREFIX = 'api.benefits_reference_data_staging' + + ## + # Hit a Benefits Reference Data End-point + # + # @path end-point [string|symbol] a string or symbol of the end-point you wish to hit. + # @params params hash [Hash] a hash of key-value pairs of parameters + # + # @return [Faraday::Response] + # + def get_data(path:, params: {}) + headers = config.base_request_headers + begin + response = perform :get, path, params, headers + rescue => e + raise BenefitsReferenceData::ServiceException.new(e), 'Lighthouse Error' + end + response + end + end + end +end diff --git a/modules/ivc_champva/app/services/ivc_champva/attachments.rb b/modules/ivc_champva/app/services/ivc_champva/attachments.rb index f5e5627f192..60bb476d2ed 100644 --- a/modules/ivc_champva/app/services/ivc_champva/attachments.rb +++ b/modules/ivc_champva/app/services/ivc_champva/attachments.rb @@ -4,7 +4,7 @@ module IvcChampva module Attachments attr_accessor :form_id, :uuid, :data - def handle_attachments(file_path) + def handle_attachments(file_path) # rubocop:disable Metrics/MethodLength file_paths = [file_path] attachments = get_attachments @@ -22,7 +22,14 @@ def handle_attachments(file_path) File.join(File.dirname(attachment), new_file_name) end - File.rename(attachment, new_file_path) + if Flipper.enabled?(:champva_pdf_decrypt, @current_user) + # Use FileUtils.mv to handle `Errno::EXDEV` error since encrypted PDFs + # get stashed in the clamav_tmp dir which is a different device on staging, apparently + FileUtils.mv(attachment, new_file_path) # Performs a copy automatically if mv fails + else + File.rename(attachment, new_file_path) + end + file_paths << new_file_path end diff --git a/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb b/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb index aead2c5aeeb..fed3cd12bd3 100644 --- a/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb +++ b/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb @@ -270,7 +270,9 @@ def get_first_name_from_user def get_personalization(first_name) if @form_number.start_with? 'vba_21_0966' - default_personalization(first_name).merge(form21_0966_personalization) + personalization = default_personalization(first_name).merge(form21_0966_personalization) + personalization.except!('lighthouse_updated_at') unless lighthouse_updated_at + personalization else default_personalization(first_name) end diff --git a/modules/simple_forms_api/spec/requests/simple_forms_api/v1/simple_forms_spec.rb b/modules/simple_forms_api/spec/requests/simple_forms_api/v1/simple_forms_spec.rb index 5ae60c00e99..cd1b8cfc624 100644 --- a/modules/simple_forms_api/spec/requests/simple_forms_api/v1/simple_forms_spec.rb +++ b/modules/simple_forms_api/spec/requests/simple_forms_api/v1/simple_forms_spec.rb @@ -916,7 +916,6 @@ 'first_name' => 'Veteran', 'date_submitted' => Time.zone.today.strftime('%B %d, %Y'), 'confirmation_number' => confirmation_number, - 'lighthouse_updated_at' => nil, 'intent_to_file_benefits' => 'survivors pension benefits', 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', @@ -942,7 +941,6 @@ 'first_name' => 'Veteran', 'date_submitted' => Time.zone.today.strftime('%B %d, %Y'), 'confirmation_number' => confirmation_number, - 'lighthouse_updated_at' => nil, 'intent_to_file_benefits' => 'survivors pension benefits', 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', diff --git a/modules/simple_forms_api/spec/services/notification_email_spec.rb b/modules/simple_forms_api/spec/services/notification_email_spec.rb index 09f93898f89..a817c0c946d 100644 --- a/modules/simple_forms_api/spec/services/notification_email_spec.rb +++ b/modules/simple_forms_api/spec/services/notification_email_spec.rb @@ -785,6 +785,7 @@ end describe '21_0966' do + let(:lighthouse_updated_at) { 1.day.ago } let(:date_submitted) { Time.zone.today.strftime('%B %d, %Y') } let(:data) do fixture_path = Rails.root.join( @@ -794,7 +795,7 @@ end let(:config) do { form_data: data, form_number: 'vba_21_0966', - confirmation_number: 'confirmation_number', date_submitted: } + confirmation_number: 'confirmation_number', date_submitted:, lighthouse_updated_at: } end let(:user) { create(:user, :loa3) } @@ -812,7 +813,7 @@ 'first_name' => 'Veteran', 'date_submitted' => date_submitted, 'confirmation_number' => 'confirmation_number', - 'lighthouse_updated_at' => nil, + 'lighthouse_updated_at' => lighthouse_updated_at, 'intent_to_file_benefits' => 'survivors pension benefits', 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', @@ -841,7 +842,7 @@ 'first_name' => 'I', 'date_submitted' => date_submitted, 'confirmation_number' => 'confirmation_number', - 'lighthouse_updated_at' => nil, + 'lighthouse_updated_at' => lighthouse_updated_at, 'intent_to_file_benefits' => 'survivors pension benefits', 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', @@ -884,7 +885,6 @@ 'first_name' => 'Veteran', 'date_submitted' => date_submitted, 'confirmation_number' => 'confirmation_number', - 'lighthouse_updated_at' => nil, 'intent_to_file_benefits' => 'survivors pension benefits', 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', @@ -913,7 +913,6 @@ 'first_name' => 'I', 'date_submitted' => date_submitted, 'confirmation_number' => 'confirmation_number', - 'lighthouse_updated_at' => nil, 'intent_to_file_benefits' => 'survivors pension benefits', 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', diff --git a/spec/controllers/v0/disability_compensation_forms_controller_spec.rb b/spec/controllers/v0/disability_compensation_forms_controller_spec.rb index 48c3a32163f..7472f584b5e 100644 --- a/spec/controllers/v0/disability_compensation_forms_controller_spec.rb +++ b/spec/controllers/v0/disability_compensation_forms_controller_spec.rb @@ -17,6 +17,7 @@ context 'evss' do before do Flipper.disable('disability_compensation_lighthouse_brd') + Flipper.disable('disability_compensation_staging_lighthouse_brd') end it 'returns separation locations' do @@ -39,6 +40,7 @@ context 'lighthouse' do before do Flipper.enable('disability_compensation_lighthouse_brd') + Flipper.disable('disability_compensation_staging_lighthouse_brd') end after(:all) do @@ -61,6 +63,34 @@ end end end + + context 'lighthouse staging' do + before do + Flipper.enable('disability_compensation_lighthouse_brd') + Flipper.enable('disability_compensation_staging_lighthouse_brd') + end + + after(:all) do + Flipper.disable('disability_compensation_lighthouse_brd') + Flipper.disable('disability_compensation_staging_lighthouse_brd') + end + + it 'returns separation locations' do + VCR.use_cassette('brd/separation_locations_staging') do + get(:separation_locations) + expect(JSON.parse(response.body)['separation_locations'].present?).to eq(true) + end + end + + it 'uses the cached response on the second request' do + VCR.use_cassette('brd/separation_locations_staging') do + 2.times do + get(:separation_locations) + expect(response.status).to eq(200) + end + end + end + end end describe '#rating_info' do diff --git a/spec/lib/disability_compensation/providers/brd/lighthouse_staging_brd_provider_spec.rb b/spec/lib/disability_compensation/providers/brd/lighthouse_staging_brd_provider_spec.rb new file mode 100644 index 00000000000..901f2aa0e5a --- /dev/null +++ b/spec/lib/disability_compensation/providers/brd/lighthouse_staging_brd_provider_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'disability_compensation/factories/api_provider_factory' +require 'disability_compensation/providers/brd/lighthouse_staging_brd_provider' +require 'support/disability_compensation_form/shared_examples/brd_provider' +require 'lighthouse/service_exception' + +RSpec.describe LighthouseStagingBRDProvider do + let(:current_user) { build(:user, :loa3) } + + before do + @provider = LighthouseStagingBRDProvider.new(current_user) + Flipper.enable(ApiProviderFactory::FEATURE_TOGGLE_BRD) + Flipper.enable(:disability_compensation_staging_lighthouse_brd) + end + + after do + Flipper.disable(ApiProviderFactory::FEATURE_TOGGLE_BRD) + Flipper.disable(:disability_compensation_staging_lighthouse_brd) + end + + it_behaves_like 'brd provider' + + it 'retrieves separation locations from the Lighthouse API' do + VCR.use_cassette('brd/separation_locations_staging') do + response = @provider.get_separation_locations + expect(response.separation_locations.length).to eq(327) + end + end +end diff --git a/spec/requests/swagger_spec.rb b/spec/requests/swagger_spec.rb index ba6c3250da0..4b579630800 100644 --- a/spec/requests/swagger_spec.rb +++ b/spec/requests/swagger_spec.rb @@ -1098,6 +1098,7 @@ Flipper.disable('disability_compensation_prevent_submission_job') Flipper.disable(ApiProviderFactory::FEATURE_TOGGLE_BRD) Flipper.disable('disability_compensation_production_tester') + Flipper.disable(:disability_compensation_staging_lighthouse_brd) end let(:form526v2) do diff --git a/spec/support/vcr_cassettes/brd/separation_locations_staging.yml b/spec/support/vcr_cassettes/brd/separation_locations_staging.yml new file mode 100644 index 00000000000..4726f5d9a98 --- /dev/null +++ b/spec/support/vcr_cassettes/brd/separation_locations_staging.yml @@ -0,0 +1,35 @@ +--- +http_interactions: + - request: + method: get + uri: https://staging-api.va.gov/services/benefits-reference-data/v1/intake-sites + body: + encoding: US-ASCII + string: '' + headers: + Apikey: + - stagingapikeyhere + User-Agent: + - Faraday v0.17.6 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: '' + headers: + Date: + - Thu, 27 Apr 2023 20:39:40 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Strict-Transport-Security: + - max-age=16000000; includeSubDomains; preload; + body: + encoding: UTF-8 + string: "{\"totalItems\":327,\"totalPages\":1,\"links\":[{\"href\":\"https://staging-api.va.gov/services/benefits-reference-data/v1/intake-sites\",\"rel\":\"self\"}],\"items\":[{\"id\":24912,\"description\":\"AF Academy\"},{\"id\":26722,\"description\":\"ANG Hub\"},{\"id\":24911,\"description\":\"Aberdeen Proving Ground\"},{\"id\":26821,\"description\":\"Al Udeid, Qatar\"},{\"id\":26742,\"description\":\"Alameda Coast Guard\"},{\"id\":24913,\"description\":\"Albuquerque\"},{\"id\":24914,\"description\":\"Altus AFB*\"},{\"id\":24915,\"description\":\"Anchorage\"},{\"id\":26767,\"description\":\"Andersen AFB - Air Force / Guam\"},{\"id\":24916,\"description\":\"Andrews AFB\"},{\"id\":24917,\"description\":\"Annapolis NS/USNA\"},{\"id\":26749,\"description\":\"Ansbach, Germany\"},{\"id\":26781,\"description\":\"Arifjan, Kuwait\"},{\"id\":24918,\"description\":\"Atlanta\"},{\"id\":26774,\"description\":\"Aviano AB, Italy\"},{\"id\":26719,\"description\":\"Bahrain / Kingdom of Bahrain\"},{\"id\":24919,\"description\":\"Baltimore\"},{\"id\":26750,\"description\":\"Bamberg, Germany\"},{\"id\":24921,\"description\":\"Barksdale AFB\"},{\"id\":26732,\"description\":\"Baumholder, Germany\"},{\"id\":26741,\"description\":\"Beale AFB\"},{\"id\":27371,\"description\":\"Beaufort Naval Hospital\"},{\"id\":26718,\"description\":\"Benelux (Brussels/Schinnen/SHAPE)\"},{\"id\":24922,\"description\":\"Bethesada NH\"},{\"id\":24923,\"description\":\"Boise\"},{\"id\":24924,\"description\":\"Bolling AFB\"},{\"id\":24925,\"description\":\"Boston\"},{\"id\":26816,\"description\":\"Buchanan\"},{\"id\":24928,\"description\":\"Buckley AGB\"},{\"id\":24929,\"description\":\"Buffalo\"},{\"id\":26795,\"description\":\"CBC Gulfport\"},{\"id\":26771,\"description\":\"Camp Atterbury (Demob)\"},{\"id\":24930,\"description\":\"Camp Casey (Korea)\"},{\"id\":24931,\"description\":\"Camp Lejeune\"},{\"id\":24932,\"description\":\"Camp Pendleton*\"},{\"id\":26798,\"description\":\"Camp Shelby (Demob)\"},{\"id\":26808,\"description\":\"Cannon AFB\"},{\"id\":26803,\"description\":\"Cape May Coast Guard\"},{\"id\":24934,\"description\":\"Carlisle Barracks\"},{\"id\":2550306,\"description\":\"Castle AFB\"},{\"id\":24936,\"description\":\"Charleston AFB\"},{\"id\":27372,\"description\":\"Charleston Naval Health Clinic\"},{\"id\":24937,\"description\":\"Cherry Point MCAS\"},{\"id\":24938,\"description\":\"Cheyenne\"},{\"id\":24939,\"description\":\"Chicago\"},{\"id\":24940,\"description\":\"Cleveland\"},{\"id\":26746,\"description\":\"Coast Guard Sector Miami\"},{\"id\":24943,\"description\":\"Columbia\"},{\"id\":26796,\"description\":\"Columbus AFB\"},{\"id\":24944,\"description\":\"Corpus Christi NAS\"},{\"id\":24946,\"description\":\"Dahlgren NSWC\"},{\"id\":24947,\"description\":\"Davis-Monthan AFB\"},{\"id\":24948,\"description\":\"Denver\"},{\"id\":24950,\"description\":\"Des Moines\"},{\"id\":24951,\"description\":\"Detroit\"},{\"id\":26791,\"description\":\"Detroit Arsenal\"},{\"id\":24952,\"description\":\"DiLorenzo-Pentagon\"},{\"id\":26772,\"description\":\"Diego Garcia / BIOT\"},{\"id\":24953,\"description\":\"Dover AFB\"},{\"id\":26787,\"description\":\"Dugway Proving Grounds\"},{\"id\":24954,\"description\":\"Dyess AFB\"},{\"id\":24955,\"description\":\"Earle Naval Weapons (new)\"},{\"id\":26733,\"description\":\"Edwards AFB\"},{\"id\":24956,\"description\":\"Eglin AFB*\"},{\"id\":26711,\"description\":\"Eielson AFB\"},{\"id\":26827,\"description\":\"Ellsworth AFB\"},{\"id\":24957,\"description\":\"Elmendorf AFB\"},{\"id\":24958,\"description\":\"Everett NS\"},{\"id\":24959,\"description\":\"Fairchild AFB*\"},{\"id\":24960,\"description\":\"Fargo\"},{\"id\":2550247,\"description\":\"Fort Barfoot (formerly Fort Pickett)\"},{\"id\":24961,\"description\":\"Fort Belvoir\"},{\"id\":24963,\"description\":\"Fort Bliss\"},{\"id\":24965,\"description\":\"Fort Campbell\"},{\"id\":24966,\"description\":\"Fort Carson\"},{\"id\":24971,\"description\":\"Fort Cavazos (formerly Fort Hood)\"},{\"id\":24967,\"description\":\"Fort Dietrick\"},{\"id\":24968,\"description\":\"Fort Dix\"},{\"id\":24969,\"description\":\"Fort Drum\"},{\"id\":24970,\"description\":\"Fort Eisenhower (formerly Fort Gordon)\"},{\"id\":24976,\"description\":\"Fort Gregg-Adams (formerly Fort Lee)\"},{\"id\":24972,\"description\":\"Fort Irwin\"},{\"id\":24973,\"description\":\"Fort Jackson\"},{\"id\":24980,\"description\":\"Fort Johnson (formerly Fort Polk)\"},{\"id\":24974,\"description\":\"Fort Knox\"},{\"id\":24975,\"description\":\"Fort Leavenworth\"},{\"id\":24977,\"description\":\"Fort Lewis\"},{\"id\":24964,\"description\":\"Fort Liberty (formerly Fort Bragg)\"},{\"id\":24978,\"description\":\"Fort Meade\"},{\"id\":24962,\"description\":\"Fort Moore (formerly Fort Benning)\"},{\"id\":24983,\"description\":\"Fort Novosel (formerly Fort Rucker)\"},{\"id\":24981,\"description\":\"Fort Richardson\"},{\"id\":24982,\"description\":\"Fort Riley\"},{\"id\":24984,\"description\":\"Fort Sam Houston\"},{\"id\":24986,\"description\":\"Fort Sill\"},{\"id\":24987,\"description\":\"Fort Stewart\"},{\"id\":2550246,\"description\":\"Fort Walker (formerly Fort A.P. Hill)\"},{\"id\":26786,\"description\":\"Ft Devens\"},{\"id\":26804,\"description\":\"Ft Hamilton\"},{\"id\":26832,\"description\":\"Ft Worth (ANG Hub)\"},{\"id\":26830,\"description\":\"Ft. Bliss\"},{\"id\":24988,\"description\":\"Ft. Eustis\"},{\"id\":24989,\"description\":\"Ft. Harrison\"},{\"id\":24990,\"description\":\"Ft. Huachuca\"},{\"id\":24991,\"description\":\"Ft. Leonard Wood\"},{\"id\":26846,\"description\":\"Ft. McCoy\"},{\"id\":26714,\"description\":\"Ft. Wainwright\"},{\"id\":26752,\"description\":\"Geilenkirchen, Germany\"},{\"id\":24992,\"description\":\"Goodfellow AFB\"},{\"id\":26799,\"description\":\"Grand Forks AFB\"},{\"id\":24993,\"description\":\"Great Lakes NTC\"},{\"id\":26748,\"description\":\"Guantanamo Bay / Cuba\"},{\"id\":26784,\"description\":\"Hanscom AFB\"},{\"id\":24994,\"description\":\"Hartford\"},{\"id\":26725,\"description\":\"Heidelberg Germany\"},{\"id\":24995,\"description\":\"Hickham AFB\"},{\"id\":24996,\"description\":\"Hill AFB\"},{\"id\":26807,\"description\":\"Holloman AFB\"},{\"id\":24997,\"description\":\"Homeland Security (HS)\"},{\"id\":24998,\"description\":\"Honolulu\"},{\"id\":24999,\"description\":\"Houston\"},{\"id\":26758,\"description\":\"Hunter AAF\"},{\"id\":25000,\"description\":\"Huntington\"},{\"id\":25001,\"description\":\"Hurlburt Field\"},{\"id\":26829,\"description\":\"Incirlik AB, Turkey\"},{\"id\":25003,\"description\":\"Indianapolis\"},{\"id\":26823,\"description\":\"JB Charleston\"},{\"id\":26824,\"description\":\"JB Charleston-AB\"},{\"id\":26838,\"description\":\"JB Langley-Eustis\"},{\"id\":26843,\"description\":\"JB Lewis-McChord\"},{\"id\":26769,\"description\":\"JB Pearl Harbor-Hickam\"},{\"id\":26840,\"description\":\"JBMH-Henderson Hall\"},{\"id\":26839,\"description\":\"JBMH-Myer\"},{\"id\":25004,\"description\":\"Jackson\"},{\"id\":25005,\"description\":\"Jacksonville NAS\"},{\"id\":26805,\"description\":\"Joint Base McGuire-Dix (Demob)\"},{\"id\":26724,\"description\":\"Kaiserslautern Germany\"},{\"id\":25006,\"description\":\"Keesler AFB\"},{\"id\":25007,\"description\":\"Key West CG\"},{\"id\":25008,\"description\":\"Key West NAS\"},{\"id\":25009,\"description\":\"Kings Bay NAS\"},{\"id\":25010,\"description\":\"Kirtland AFB\"},{\"id\":25011,\"description\":\"Kitsap NS* (formerly Bremerton NB)\"},{\"id\":26779,\"description\":\"Kunsan AB, Korea\"},{\"id\":25012,\"description\":\"Lackland AFB\"},{\"id\":26820,\"description\":\"Lajes Field\"},{\"id\":25013,\"description\":\"Lakehurst (Southern NJ)\"},{\"id\":26735,\"description\":\"Landstuhl Reg Med Ctr\"},{\"id\":25231,\"description\":\"Langley Air Force Base\"},{\"id\":26834,\"description\":\"Laughlin AFB\"},{\"id\":25016,\"description\":\"Lemoore NAS\"},{\"id\":25017,\"description\":\"Lincoln\"},{\"id\":25019,\"description\":\"Little Rock\"},{\"id\":25020,\"description\":\"Little Rock AFB\"},{\"id\":25021,\"description\":\"Los Angeles\"},{\"id\":26727,\"description\":\"Los Angeles AFB\"},{\"id\":25022,\"description\":\"Louisville\"},{\"id\":26723,\"description\":\"Luke AFB\"},{\"id\":25029,\"description\":\"MC Recruit Depot\"},{\"id\":26738,\"description\":\"MCAGTC 29 Palms\"},{\"id\":26826,\"description\":\"MCAS Beaufort\"},{\"id\":26720,\"description\":\"MCAS Yuma\"},{\"id\":25030,\"description\":\"MCB Hawaii\"},{\"id\":26842,\"description\":\"MCB Quantico\"},{\"id\":26760,\"description\":\"MCLB Albany\"},{\"id\":26729,\"description\":\"MCRD San Diego\"},{\"id\":25023,\"description\":\"MacDill AFB\"},{\"id\":25024,\"description\":\"Malmstrom AFB\"},{\"id\":25025,\"description\":\"Manchester\"},{\"id\":25026,\"description\":\"Manila\"},{\"id\":25027,\"description\":\"Maxwell AFB\"},{\"id\":25028,\"description\":\"Mayport NS*\"},{\"id\":25031,\"description\":\"McChord AFB\"},{\"id\":25032,\"description\":\"McConnell AFB\"},{\"id\":25033,\"description\":\"McGuire AFB\"},{\"id\":26757,\"description\":\"Miami\"},{\"id\":25034,\"description\":\"Miami CG\"},{\"id\":25035,\"description\":\"Milwaukee\"},{\"id\":26800,\"description\":\"Minot AFB\"},{\"id\":25036,\"description\":\"Miramar MCAS\"},{\"id\":25037,\"description\":\"Montgomery\"},{\"id\":26755,\"description\":\"Moody AFB\"},{\"id\":25038,\"description\":\"Mountain Home AFB\"},{\"id\":25039,\"description\":\"Muskogee\"},{\"id\":26811,\"description\":\"NAS Fallon / NV\"},{\"id\":26833,\"description\":\"NAS JRB Fort Worth\"},{\"id\":26782,\"description\":\"NAS JRB New Orleans\"},{\"id\":26835,\"description\":\"NAS Kingsville\"},{\"id\":26797,\"description\":\"NAS Meridian\"},{\"id\":26766,\"description\":\"NAVBASE Guam Barrigada / Guam\"},{\"id\":26736,\"description\":\"NAWS China Lake\"},{\"id\":26721,\"description\":\"NB San Diego\"},{\"id\":26822,\"description\":\"NS Newport\"},{\"id\":25049,\"description\":\"NSA\"},{\"id\":26828,\"description\":\"NSA Midsouth Memphis\"},{\"id\":26765,\"description\":\"NSA Souda Bay / Greece\"},{\"id\":26801,\"description\":\"NSY Portsmouth\"},{\"id\":26775,\"description\":\"Naples / ITALY\"},{\"id\":25041,\"description\":\"Nashville\"},{\"id\":26871,\"description\":\"National Capital Region (NCR)\"},{\"id\":26802,\"description\":\"Naval Base Ventura County\"},{\"id\":26810,\"description\":\"Nellis AFB, NV/Creech AFB, NV\"},{\"id\":25043,\"description\":\"New London NSB\"},{\"id\":25044,\"description\":\"New Orleans\"},{\"id\":25045,\"description\":\"New River MCAS\"},{\"id\":25046,\"description\":\"New York\"},{\"id\":25047,\"description\":\"Newark\"},{\"id\":25048,\"description\":\"Norfolk NB\"},{\"id\":26831,\"description\":\"North Ft. Hood\"},{\"id\":25051,\"description\":\"Oakland\"},{\"id\":25053,\"description\":\"Offutt AFB\"},{\"id\":26780,\"description\":\"Osan AB, Korea\"},{\"id\":26849,\"description\":\"Other - AGR\"},{\"id\":26850,\"description\":\"Other - Not on List\"},{\"id\":25054,\"description\":\"Patrick AFB\"},{\"id\":25055,\"description\":\"Patuxnet River NAS\"},{\"id\":25056,\"description\":\"Pearl Harbor NB\"},{\"id\":25057,\"description\":\"Pensacola NAS\"},{\"id\":25058,\"description\":\"Pensacola NH\"},{\"id\":27373,\"description\":\"Pentagon In-Take Site\"},{\"id\":26743,\"description\":\"Petaluma Coast Guard\"},{\"id\":25059,\"description\":\"Peterson AFB\"},{\"id\":25060,\"description\":\"Philadelphia\"},{\"id\":25061,\"description\":\"Phoenix\"},{\"id\":25062,\"description\":\"Pittsburgh\"},{\"id\":25064,\"description\":\"Portland\"},{\"id\":25065,\"description\":\"Portsmouth NAS\"},{\"id\":25066,\"description\":\"Portsmouth NH\"},{\"id\":26740,\"description\":\"Presidio of Monterey\"},{\"id\":25067,\"description\":\"Providence\"},{\"id\":26762,\"description\":\"RAF Alconbury, UK\"},{\"id\":26761,\"description\":\"RAF Lakenheath, UK\"},{\"id\":26763,\"description\":\"RAF Menwith Hill, UK\"},{\"id\":26764,\"description\":\"RAF Mildenhall, UK\"},{\"id\":26753,\"description\":\"Ramstein AB, Germany\"},{\"id\":25068,\"description\":\"Randolph AFB\"},{\"id\":25069,\"description\":\"Redstone Arsenal\"},{\"id\":25070,\"description\":\"Reno\"},{\"id\":25071,\"description\":\"Roanoke\"},{\"id\":26759,\"description\":\"Robins AFB\"},{\"id\":26770,\"description\":\"Rock Island\"},{\"id\":26737,\"description\":\"Rota / SPAIN\"},{\"id\":25072,\"description\":\"Salt Lake City\"},{\"id\":25073,\"description\":\"San Diego\"},{\"id\":25079,\"description\":\"San Francisco\"},{\"id\":25080,\"description\":\"San Juan\"},{\"id\":25075,\"description\":\"Schofield Barracks\"},{\"id\":25076,\"description\":\"Schriever AFB\"},{\"id\":26734,\"description\":\"Schweinfurt Germany\"},{\"id\":25077,\"description\":\"Scotia ND(now Saratoga Springs Transition Station)\"},{\"id\":25078,\"description\":\"Scott AFB\"},{\"id\":25081,\"description\":\"Seattle\"},{\"id\":25083,\"description\":\"Seymour Johnson AFB\"},{\"id\":25084,\"description\":\"Shaw AFB\"},{\"id\":25085,\"description\":\"Sheppard AFB\"},{\"id\":26776,\"description\":\"Sigonella / ITALY\"},{\"id\":25086,\"description\":\"Sioux Falls\"},{\"id\":26751,\"description\":\"Spangdahlem AB, Germany\"},{\"id\":25088,\"description\":\"St. Louis\"},{\"id\":25089,\"description\":\"St. Paul\"},{\"id\":25090,\"description\":\"St. Petersburg\"},{\"id\":26731,\"description\":\"Stuttgart Germany\"},{\"id\":26837,\"description\":\"Texas City Coast Guard\"},{\"id\":25092,\"description\":\"Tinker AFB\"},{\"id\":25093,\"description\":\"Togus\"},{\"id\":25094,\"description\":\"Travis AFB\"},{\"id\":25095,\"description\":\"Tripler AMC\"},{\"id\":25096,\"description\":\"Tyndall AFB\"},{\"id\":26817,\"description\":\"USCG Air Station Ramey\"},{\"id\":26806,\"description\":\"USCG Atlantic City\"},{\"id\":26788,\"description\":\"USCG Baltimore\"},{\"id\":26728,\"description\":\"USCG Base Los Angeles\"},{\"id\":26819,\"description\":\"USCG Borinquen\"},{\"id\":26785,\"description\":\"USCG Boston\"},{\"id\":26812,\"description\":\"USCG Buffalo\"},{\"id\":26825,\"description\":\"USCG Charleston\"},{\"id\":26744,\"description\":\"USCG Clearwater\"},{\"id\":26814,\"description\":\"USCG Cleveland\"},{\"id\":26836,\"description\":\"USCG Corpus Christi\"},{\"id\":26790,\"description\":\"USCG Detroit\"},{\"id\":26792,\"description\":\"USCG Grand Haven\"},{\"id\":26768,\"description\":\"USCG Honolulu\"},{\"id\":26745,\"description\":\"USCG Humboldt Bay\"},{\"id\":26756,\"description\":\"USCG Jacksonville\"},{\"id\":26709,\"description\":\"USCG Juneau\"},{\"id\":26710,\"description\":\"USCG Ketchikan\"},{\"id\":26712,\"description\":\"USCG Kodiak\"},{\"id\":26813,\"description\":\"USCG Long Island\"},{\"id\":26848,\"description\":\"USCG Martinsburg\"},{\"id\":26847,\"description\":\"USCG Milwaukee\"},{\"id\":26715,\"description\":\"USCG Mobile\"},{\"id\":26783,\"description\":\"USCG New Orleans\"},{\"id\":26815,\"description\":\"USCG Portland\"},{\"id\":26845,\"description\":\"USCG Portsmouth\"},{\"id\":26841,\"description\":\"USCG Recruiting Command\"},{\"id\":26789,\"description\":\"USCG SW Harbor\"},{\"id\":26730,\"description\":\"USCG San Diego\"},{\"id\":26747,\"description\":\"USCG Savannah\"},{\"id\":26844,\"description\":\"USCG Seattle\"},{\"id\":26713,\"description\":\"USCG Sitka\"},{\"id\":26794,\"description\":\"USCG St Louis\"},{\"id\":26818,\"description\":\"USCG Station San Juan\"},{\"id\":26793,\"description\":\"USCG Su Ste Marie\"},{\"id\":26716,\"description\":\"USCG Valdez\"},{\"id\":27374,\"description\":\"VAMC Biloxi\"},{\"id\":25097,\"description\":\"Vance AFB\"},{\"id\":26739,\"description\":\"Vandenberg AFB\"},{\"id\":26773,\"description\":\"Vicenza Italy\"},{\"id\":26726,\"description\":\"Vilseck Germany\"},{\"id\":25098,\"description\":\"Waco\"},{\"id\":25099,\"description\":\"Walter Reed AMC - C&P\"},{\"id\":25100,\"description\":\"Warren AFB (Cheyenne)\"},{\"id\":25102,\"description\":\"West Point\"},{\"id\":25103,\"description\":\"Whidbey Island NAS\"},{\"id\":25105,\"description\":\"White River Junction\"},{\"id\":26809,\"description\":\"White Sands\"},{\"id\":25104,\"description\":\"Whiteman AFB\"},{\"id\":25106,\"description\":\"Whiting Field\"},{\"id\":25107,\"description\":\"Wichita\"},{\"id\":26754,\"description\":\"Wiesbaden Germany\"},{\"id\":25109,\"description\":\"Wilmington\"},{\"id\":25110,\"description\":\"Winston-Salem\"},{\"id\":25111,\"description\":\"Wright-Patterson AFB\"},{\"id\":26777,\"description\":\"Yokosuka / JAPAN\"},{\"id\":26778,\"description\":\"Yokota AB, Japan\"},{\"id\":25112,\"description\":\"Yongsan (Korea)\"},{\"id\":26717,\"description\":\"Yuma Proving Grounds\"}]}" + recorded_at: Thu, 27 Apr 2023 20:39:40 GMT +recorded_with: VCR 6.1.0