class Mirah::Client

A client designed to communicate with the Mirah system in a constrained set of well defined ways. This is a front to the more general Graphql backend which is available.

Patient Methods

Organization Methods

Practitioner Methods

Appointment Methods

@example

# Create a new client
client = Mirah::Client.new(host: 'https://api.mirah.com', user_id: 'my_user_id', access_token: 'my_token')

# find a patient
client.find_patient_by_external_id('mrn0001')

Attributes

client[R]

Access to the underlying graphql client. You may use this for advanced queries that are not provided as part of the standard library.

client_context[R]

Public Class Methods

new(host:, user_id:, access_token:) click to toggle source

Construct a new Client with the given host and credentials. @param host [String] The host, e.g. 'api.mirah.com' @param user_id [String] Your user id @param access_token [String] Your secret API token.

# File lib/mirah/client.rb, line 42
def initialize(host:, user_id:, access_token:)
  @client = Graphql.create_client(host: host)
  @client_context = { user_id: user_id, access_token: access_token }
end

Public Instance Methods

find_appointment(id) click to toggle source

Find an appointment by the given Mirah internal UUID. This method should be used if you already know the Mirah identifier. If you wish to query by your own system identifier, you should use {#find_appointment_by_external_id}

@since 0.1.0 @param id [String] Mirah UUID for the appointment @return [Data::Appointment, nil] the appointment, or nil if the record does not exist.

# File lib/mirah/client.rb, line 248
def find_appointment(id)
  query_record(Graphql::Queries::AppointmentIdQuery, id, Data::Appointment, 'appointment')
end
find_appointment_by_external_id(external_id) click to toggle source

Find an appointment by your external id. This is a convenience method. If you wish to query a list of appointments by external id, please use {Client#query_appointments}.

@since 0.1.0 @param external_id [String] The identifier of the system of record @return [Data::Appointment, nil] the appointment, or nil if the record does not exist.

# File lib/mirah/client.rb, line 258
def find_appointment_by_external_id(external_id)
  query_record_by_external_id(Graphql::Queries::AppointmentExternalIdQuery,
                              external_id,
                              Data::Appointment,
                              'appointmentExternal')
end
find_organization(id) click to toggle source

Find an organization by the given Mirah internal UUID. This method should be used if you already know the Mirah identifier. If you wish to query by your own system identifier, you should use {#find_organization_by_external_id}

@since 0.1.0 @param id [String] Mirah UUID for the organization @return [Data::Organization, nil] the organization, or nil if the record does not exist.

# File lib/mirah/client.rb, line 125
def find_organization(id)
  query_record(Graphql::Queries::OrganizationIdQuery, id, Data::Organization, 'organization')
end
find_organization_by_external_id(external_id) click to toggle source

Find an organization by your external id. This is a convenience method. If you wish to query a list of organizations by external id, please use {Client#query_organizations}.

@since 0.1.0 @param external_id [String] The identifier of the system of record @return [Data::Organization, nil] the organization, or nil if the record does not exist.

# File lib/mirah/client.rb, line 135
def find_organization_by_external_id(external_id)
  query_record_by_external_id(Graphql::Queries::OrganizationExternalIdQuery,
                              external_id,
                              Data::Organization,
                              'organizationExternal')
end
find_patient(id) click to toggle source

Find a patient by the given Mirah internal UUID. This method should be used if you already know the Mirah identifier. If you wish to query by your own system identifier, you should use {#find_patient_by_external_id}

@since 0.1.0 @param id [String] Mirah UUID for the patient @return [Data::Patient, nil] the patient, or nil if the record does not exist.

# File lib/mirah/client.rb, line 61
def find_patient(id)
  query_record(Graphql::Queries::PatientIdQuery, id, Data::Patient, 'patient')
end
find_patient_by_external_id(external_id) click to toggle source

Find a patient by your external id. This is a convenience method. If you wish to query a list of patients by external id, please use {Client#query_patients}.

@since 0.1.0 @param external_id [String] The identifier of the system of record @return [Data::Patient, nil] the patient, or nil if the record does not exist.

# File lib/mirah/client.rb, line 71
def find_patient_by_external_id(external_id)
  query_record_by_external_id(Graphql::Queries::PatientExternalIdQuery,
                              external_id,
                              Data::Patient,
                              'patientExternal')
end
find_practitioner(id) click to toggle source

Find an practitioner by the given Mirah internal UUID. This method should be used if you already know the Mirah identifier. If you wish to query by your own system identifier, you should use {#find_practitioner_by_external_id}

@since 0.1.0 @param id [String] Mirah UUID for the practitioner @return [Data::Practitioner, nil] the practitioner, or nil if the record does not exist.

# File lib/mirah/client.rb, line 183
def find_practitioner(id)
  query_record(Graphql::Queries::PractitionerIdQuery, id, Data::Practitioner, 'practitioner')
end
find_practitioner_by_external_id(external_id) click to toggle source

Find an practitioner by your external id. This is a convenience method. If you wish to query a list of practitioners by external id, please use {Client#query_practitioners}.

@since 0.1.0 @param external_id [String] The identifier of the system of record @return [Data::Practitioner, nil] the practitioner, or nil if the record does not exist.

# File lib/mirah/client.rb, line 193
def find_practitioner_by_external_id(external_id)
  query_record_by_external_id(Graphql::Queries::PractitionerExternalIdQuery,
                              external_id,
                              Data::Practitioner,
                              'practitionerExternal')
end
push_appointment(external_id:, status:, **input_values) click to toggle source

Create or update an appointment. You must specify an external identifier, all other parameters are optional, but you may receive errors if you attempt to specify too few parameters for an appointment that does not exist.

@since 0.1.0 @param external_id [String] the external identifier for this appointment @param status [String] the status identifier of this appointment, see {Data::Appointment#status} @option input_values [String, nil] :start_date see {Data::Appointment#start_date} @option input_values [String, nil] :end_date see {Data::Appointment#end_date} @option input_values [String, nil] :minutes_duration see {Data::Appointment#minutes_duration} @option input_values [String, nil] :patient_id see {Data::Appointment#patient_id} @option input_values [String, nil] :external_patient_id see {Data::Appointment#external_patient_id} @option input_values [String, nil] :practitioner_id see {Data::Appointment#practitioner_id} @option input_values [String, nil] :external_practitioner_id see {Data::Appointment#external_practitioner_id} @option input_values [String, nil] :organization_id see {Data::Appointment#organization_id} @option input_values [String, nil] :external_organization_id see {Data::Appointment#external_organization_id} @return [PushResult<Data::Appointment>] the operation result with the appointment on success

# File lib/mirah/client.rb, line 298
def push_appointment(external_id:, status:, **input_values)
  mutate(Graphql::Mutations::CreateOrUpdateAppointmentMutation,
         Inputs::AppointmentInput.new(input_values.merge(external_id: external_id, status: status)),
         Data::Appointment, 'createOrUpdateAppointment')
end
push_organization(external_id:, **input_values) click to toggle source

Create or update an organization. You must specify an external identifier, all other parameters are optional, but you may receive errors if you attempt to specify too few parameters for an organization that does not exist.

@since 0.1.0 @param external_id [String] the external identifier for this organization @option input_values [String, nil] :name see {Data::Organization#name} @option input_values [String, nil] :external_part_of_id The external identifier for the parent organization @return [PushResult<Data::Organization>] the operation result with the organization on success

# File lib/mirah/client.rb, line 167
def push_organization(external_id:, **input_values)
  mutate(Graphql::Mutations::CreateOrUpdateOrganizationMutation,
         Inputs::OrganizationInput.new(input_values.merge(external_id: external_id)),
         Data::Organization, 'createOrUpdateOrganization')
end
push_patient(external_id:, **input_values) click to toggle source

Create or update a patient. You must specify an external identifier, all other parameters are optional, but you may receive errors if you attempt to specify too few parameters for a patient that does not exist.

@since 0.1.0 @param external_id [String] the external identifier for this patient @option input_values [String, nil] :given_name see {Data::Patient#given_name} @option input_values [String, nil] :family_name see {Data::Patient#family_name} @option input_values [Date, nil] :birth_date see {Data::Patient#birth_date} @option input_values [String, nil] :gender see {Data::Patient#gender} @option input_values [String, nil] :primary_language see {Data::Patient#primary_language} @option input_values [String, nil] :email see {Data::Patient#email} @option input_values [String, nil] :phone_number see {Data::Patient#phone_number} @option input_values [String, nil] :external_managing_organization_id see {Data::Patient#external_managing_organization_id} @return [PushResult<Data::Patient>] the operation result with a patient on success

# File lib/mirah/client.rb, line 109
def push_patient(external_id:, **input_values)
  mutate(Graphql::Mutations::CreateOrUpdatePatientMutation,
         Inputs::PatientInput.new(input_values.merge(external_id: external_id)),
         Data::Patient, 'createOrUpdatePatient')
end
push_practitioner(external_id:, **input_values) click to toggle source

Create or update an practitioner. You must specify an external identifier, all other parameters are optional, but you may receive errors if you attempt to specify too few parameters for an practitioner that does not exist.

@since 0.1.0 @param external_id [String] the external identifier for this practitioner @option input_values [String, nil] :given_name see {Data::Practitioner#given_name} @option input_values [String, nil] :family_name see {Data::Practitioner#family_name} @option input_values [String, nil] :title see {Data::Practitioner#title} @option input_values [String, nil] :suffix see {Data::Practitioner#suffix} @option input_values [String, nil] :email see {Data::Practitioner#email} @option input_values [String, nil] :default_practitioner_role see {Data::Practitioner#default_practitioner_role} @option input_values [String, nil] :sso_username see {Data::Practitioner#sso_username} @option input_values [Array<String>, nil] :external_organization_ids see

{Data::Practitioner#external_organization_ids}

@return [PushResult<Data::Practitioner>] the operation result with the practitioner on success

# File lib/mirah/client.rb, line 232
def push_practitioner(external_id:, **input_values)
  mutate(Graphql::Mutations::CreateOrUpdatePractitionerMutation,
         Inputs::PractitionerInput.new(input_values.merge(external_id: external_id)),
         Data::Practitioner, 'createOrUpdatePractitioner')
end
query_appointments(external_id: nil, status: nil) click to toggle source

@since 0.1.0 @param external_id [Array<String>] See {Mirah::Filters::AppointmentFilters#external_id} @param status [String] See {Mirah::Filters::AppointmentFilters#external_id} @return [Collection<Data::Appointment>] a collection of pageable appointments.

# File lib/mirah/client.rb, line 272
def query_appointments(external_id: nil, status: nil)
  query_connection(
    Graphql::Queries::AppointmentQuery,
    Filters::AppointmentFilters.new(external_id: external_id, status: status),
    Filters::Paging.default,
    Data::Appointment,
    'appointments'
  )
end
query_connection(query, input, paging, data_klass, path) click to toggle source

This is technically a public method so that collections can get the next page, but should not generally be invoked directly otherwise. @private

# File lib/mirah/client.rb, line 311
def query_connection(query, input, paging, data_klass, path)
  variables = input.to_graphql_hash.merge(paging.to_graphql_hash)
  response = client.query(query, variables: variables, context: client_context)
  check_response_for_errors(response)

  response_json = response.to_h
  results = parse_graphql_connection_response(response_json, data_klass, path, 'nodes')
  page_info = parse_page_info(response_json, path)

  # Used to generate subsequent pages
  query_details = { query: query, input: input, paging: paging, data_klass: data_klass, path: path }
  Collection.new(results: results, page_info: page_info, client: self, query: query_details)
rescue StandardError => e
  handle_errors(e)
end
query_organizations(external_id: nil, search: nil) click to toggle source

@since 0.1.0 @param external_id [Array<String>] See {Mirah::Filters::OrganizationFilters#external_id} @param search [String] See {Mirah::Filters::OrganizationFilters#search} @return [Collection<Data::Organization>] a collection of pageable organizations.

# File lib/mirah/client.rb, line 149
def query_organizations(external_id: nil, search: nil)
  query_connection(
    Graphql::Queries::OrganizationQuery,
    Filters::OrganizationFilters.new(external_id: external_id, search: search),
    Filters::Paging.default,
    Data::Organization,
    'organizations'
  )
end
query_patients(external_id: nil, search: nil) click to toggle source

Query for patients. You may specify a set of parameters as defined in {Mirah::Filters::PatientFilters}. Results are returned in a paginated format. See {Collection} for how to page results.

@since 0.1.0 @param external_id [Array<String>] See {Mirah::Filters::PatientFilters#external_id} @param search [String] See {Mirah::Filters::PatientFilters#search} @return [Collection<Data::Patient>] a collection of pageable patients.

# File lib/mirah/client.rb, line 85
def query_patients(external_id: nil, search: nil)
  query_connection(
    Graphql::Queries::PatientQuery,
    Filters::PatientFilters.new(external_id: external_id, search: search),
    Filters::Paging.default,
    Data::Patient,
    'patients'
  )
end
query_practitioners(external_id: nil, search: nil) click to toggle source

Query for practitioners. You may specify a set of parameters as defined in {Mirah::Filters::PractitionerFilters}. Results are returned in a paginated format. See {Collection} for how to page results.

@since 0.1.0 @param external_id [String] See {Mirah::Filters::PractitionerFilters#external_id} @param search [String] See {Mirah::Filters::PractitionerFilters#search} @return [Collection<Data::Practitioner>] a collection of pageable practitioners.

# File lib/mirah/client.rb, line 207
def query_practitioners(external_id: nil, search: nil)
  query_connection(
    Graphql::Queries::PractitionerQuery,
    Filters::PractitionerFilters.new(external_id: external_id, search: search),
    Filters::Paging.default,
    Data::Practitioner,
    'practitioners'
  )
end

Private Instance Methods

check_response_for_errors(response) click to toggle source
# File lib/mirah/client.rb, line 409
def check_response_for_errors(response)
  if response.errors.any?
    if response.errors[:data] == ['401 Unauthorized'] # rubocop:disable Style/GuardClause
      raise Errors::InvalidCredentials, 'The credentials you have supplied are invalid'
    else
      raise Errors::ServerError, 'Unknown error from Mirah server: ' + response.errors.values.flatten.join(',')
    end
  end

  raise Errors::ServerError, 'Data payload was empty' unless response.data
end
handle_errors(e) click to toggle source

Check that any errors raised have been wrapped in Mirah errors appropriately.

# File lib/mirah/client.rb, line 400
def handle_errors(e)
  case e
  when Error
    raise
  else
    raise Errors::ClientError, e
  end
end
mutate(mutation, input, data_klass, path) click to toggle source
# File lib/mirah/client.rb, line 351
def mutate(mutation, input, data_klass, path)
  input.validate!
  response = client.query(mutation, variables: { input: input.to_graphql_hash }, context: client_context)

  response_json = response.to_h.dig('data', path)

  if response_json
    result = data_klass.from_graphql_hash(response_json['result']) if response_json['result']

    status = response_json['status']
    errors = response_json['errors']
  else
    status = 'ERROR'
    errors = response.to_h['errors']
  end

  PushResult.new(result: result, status: status, errors: errors, input: input)
rescue StandardError => e
  handle_errors(e)
end
parse_graphql_connection_response(response_json, data_klass, path, item = 'nodes') click to toggle source

Parse the main part of the graphql response into the appropriate data class with additional paging and connection information.

@param response_json [GraphQL::Client::Response] the graphql error response @param data_klass [Class] the class to transform the response into @param path [String] the path in the response to look for the raw data @return [Collection<Class>] a wrapped collection with the results

# File lib/mirah/client.rb, line 393
def parse_graphql_connection_response(response_json, data_klass, path, item = 'nodes')
  response_json.dig('data', path, item)&.map do |datum|
    data_klass.from_graphql_hash(datum.to_h)
  end
end
parse_graphql_response(response_json, data_klass, path) click to toggle source

Parse the main part of the graphql response into the appropriate data class.

@param response_json [GraphQL::Client::Response] the graphql error response @param data_klass [Class] the class to transform the response into @param path [String] the path in the response to look for the raw data @return [Class] an item of the type of `data_klass`

# File lib/mirah/client.rb, line 378
def parse_graphql_response(response_json, data_klass, path)
  data_klass.from_graphql_hash(response_json.dig('data', path))
end
parse_page_info(response_json, path) click to toggle source
# File lib/mirah/client.rb, line 382
def parse_page_info(response_json, path)
  Data::PageInfo.from_graphql_hash(response_json.dig('data', path, 'pageInfo'))
end
query_record(query, id, data_klass, path) click to toggle source
# File lib/mirah/client.rb, line 331
def query_record(query, id, data_klass, path)
  response = client.query(query, variables: { id: id }, context: client_context)
  check_response_for_errors(response)
  response_json = response.to_h

  parse_graphql_response(response_json, data_klass, path)
rescue StandardError => e
  handle_errors(e)
end
query_record_by_external_id(query, external_id, data_klass, path) click to toggle source
# File lib/mirah/client.rb, line 341
def query_record_by_external_id(query, external_id, data_klass, path)
  response = client.query(query, variables: { externalId: external_id }, context: client_context)
  check_response_for_errors(response)
  response_json = response.to_h

  parse_graphql_response(response_json, data_klass, path)
rescue StandardError => e
  handle_errors(e)
end