Ejemplo n.º 1
0
def split_if_relative_reference(reference: message.Message):
    """Splits relative references into their components.

  For example, a reference with the uri set to "Patient/ABCD" will be changed
  into a reference with the patientId set to "ABCD".

  Args:
    reference: The FHIR reference to potentially split.
  """
    _validate_reference(reference)
    uri_field = reference.DESCRIPTOR.fields_by_name.get('uri')
    if not proto_utils.field_is_set(reference, uri_field):
        return  # No URI to split

    uri = proto_utils.get_value_at_field(reference, uri_field)

    # We're permissive about various full URL schemes. If we're able to find a
    # match, the URI is valid as-is.
    url_match = re.fullmatch(_URL_REFERENCE_PATTERN, uri.value)
    if url_match is not None:
        return  # URI is valid

    internal_match = re.fullmatch(_INTERNAL_REFERENCE_PATTERN, uri.value)
    if internal_match is not None:
        # Note that we make the reference_id off of the reference before adding it,
        # since adding the reference_id would destroy the uri field, as they are
        # both in the same oneof. This allows us to copy fields from uri to
        # reference_id without making an extra copy.
        reference_id_field = get_reference_id_field_for_resource(
            reference, internal_match.group('resource_type'))
        reference_id = proto_utils.create_message_from_descriptor(
            reference_id_field.message_type)
        populate_typed_reference_id(reference_id,
                                    internal_match.group('resource_id'),
                                    internal_match.group('version'))

        proto_utils.copy_common_field(uri, reference_id, 'id')
        proto_utils.copy_common_field(uri, reference_id, 'extension')
        proto_utils.set_value_at_field(reference, reference_id_field,
                                       reference_id)
        return

    fragment_match = re.fullmatch(_FRAGMENT_REFERENCE_PATTERN, uri.value)
    if fragment_match is not None:
        # Note that we make the fragment off of the reference before adding it,
        # since adding the fragment would destroy the uri field, as they are both in
        # the same oneof. This allows us to copy fields from uri to fragment without
        # making an extra copy.
        fragment_field = reference.DESCRIPTOR.fields_by_name['fragment']
        fragment = proto_utils.create_message_from_descriptor(
            fragment_field.message_type)

        value_field = fragment.DESCRIPTOR.fields_by_name['value']
        proto_utils.set_value_at_field(fragment, value_field, uri.value[1:])
        proto_utils.copy_common_field(uri, fragment, 'id')
        proto_utils.copy_common_field(uri, fragment, 'extension')
        proto_utils.set_value_at_field(reference, fragment_field, fragment)
        return

    raise ValueError(f'String {uri.value!r} cannot be parsed as a reference.')
Ejemplo n.º 2
0
    def _parse_field_value(self, field: descriptor.FieldDescriptor,
                           json_value: Any) -> message.Message:
        """Returns a new Message described by the FieldDescriptor and json_value.

    Args:
      field: The FieldDescriptor of the Message instance to create.
      json_value: The JSON value representation to merge into the newly created
        Message.

    Returns:
      A new Message as described by the provided FieldDescriptor merged with the
      contents of json_value.
    """
        if field.type != descriptor.FieldDescriptor.TYPE_MESSAGE:
            raise ValueError('Error in FHIR proto definition, field: '
                             f'{field.full_name} is not a message.')
        if field.message_type.full_name == any_pb2.Any.DESCRIPTOR.full_name:
            contained = self.primitive_handler.new_contained_resource()
            self._merge_contained_resource(json_value, contained)

            any_message = any_pb2.Any()
            any_message.Pack(contained)
            return any_message
        else:
            target = proto_utils.create_message_from_descriptor(
                field.message_type)
            self.merge_value(json_value, target)
            return target
Ejemplo n.º 3
0
        def test_no_value_behaviour_valid(self):
            for primitive_data in self.primitive_data_list:

                file_name = primitive_data.name
                field_type = primitive_data.field_type

                # Xhtml cannot have extentions. It is always consdered to have a value.
                # So we are not testing it here.
                if file_name == 'Xhtml':
                    continue

                # It's ok to have no value if there's at least another extension present
                # (More than one extension).
                primitive_with_no_value = field_type()

                self._set_primitive_has_no_value_extension(
                    primitive_with_no_value)

                # Add arbitrary extension.
                extensions_field = primitive_with_no_value.DESCRIPTOR.fields_by_name[
                    'extension']
                extension = proto_utils.create_message_from_descriptor(
                    extensions_field.message_type)
                cast(Any, extension).url.value = 'abcd'
                cast(Any, extension).value.boolean.value = True

                proto_utils.append_value_at_field(primitive_with_no_value,
                                                  'extension', extension)
                _ = self.json_format.print_fhir_to_json_string(
                    primitive_with_no_value)
Ejemplo n.º 4
0
  def testCreateMessageFromDescriptor_withArguments_returnsMessage(self):
    """Tests that the correct class is instantiated with kwargs."""

    patient_name = datatypes_pb2.HumanName(
        text=datatypes_pb2.String(value="Foo"),
        family=datatypes_pb2.String(value="Bar"))
    expected_patient = patient_pb2.Patient(name=[patient_name])
    actual_patient = proto_utils.create_message_from_descriptor(
        patient_pb2.Patient.DESCRIPTOR, name=[patient_name])
    self.assertEqual(expected_patient, actual_patient)
Ejemplo n.º 5
0
def create_primitive_has_no_value(
        desc: descriptor.Descriptor) -> message.Message:
    """Returns a PrimitiveHasNoValue extension provided a descriptor."""
    primitive_has_no_value = proto_utils.create_message_from_descriptor(desc)

    # Silencing the type checkers as pytype doesn't fully support structural
    # subtyping yet.
    # See: https://mypy.readthedocs.io/en/stable/casts.html#casts-and-type-assertions.
    # Soon: https://www.python.org/dev/peps/pep-0544/.
    cast(Any, primitive_has_no_value).url.value = PRIMITIVE_HAS_NO_VALUE_URL
    cast(Any, primitive_has_no_value).value.boolean.value = True

    return primitive_has_no_value
Ejemplo n.º 6
0
    def test_no_value_behaviour_valid(self, field_type: Type[message.Message]):
        # It's ok to have no value if there's another extension present
        # (Two extensions in total).
        primitive_with_no_value = field_type()

        self._set_primitive_has_no_value_extension(primitive_with_no_value)

        # Add arbitrary extension.
        extensions_field = primitive_with_no_value.DESCRIPTOR.fields_by_name[
            'extension']
        extension = proto_utils.create_message_from_descriptor(
            extensions_field.message_type)
        cast(Any, extension).url.value = 'abcd'
        cast(Any, extension).value.boolean.value = True

        proto_utils.append_value_at_field(primitive_with_no_value, 'extension',
                                          extension)
        _ = json_format.print_fhir_to_json_string(primitive_with_no_value)
Ejemplo n.º 7
0
  def get_element(self) -> Optional[message.Message]:
    """Returns the raw Element underlying the wrapped primitive.

    Note that conversion-only extensions are removed prior to returning.
    """
    if not (proto_utils.field_is_set(self.wrapped, 'id') or
            proto_utils.field_is_set(self.wrapped, 'extension')):
      return None  # Early-exit if we can't populate an Element

    element = proto_utils.create_message_from_descriptor(
        self.wrapped.DESCRIPTOR)
    if proto_utils.field_is_set(self.wrapped, 'id'):
      proto_utils.copy_common_field(self.wrapped, element, 'id')

    extensions_list = cast(List[Any],
                           extensions.get_fhir_extensions(self.wrapped))
    for extension in extensions_list:
      if extension.url.value not in extensions.CONVERSION_ONLY_EXTENSION_URLS:
        proto_utils.append_value_at_field(element, 'extension', extension)

    return element
Ejemplo n.º 8
0
 def testCreateMessageFromDescriptor_returnsMessage(self):
   """Tests that the correct class is returned for a message."""
   self.assertEqual(
       proto_utils.create_message_from_descriptor(
           patient_pb2.Patient.DESCRIPTOR), patient_pb2.Patient())
Ejemplo n.º 9
0
def split_if_relative_reference(reference: message.Message):
  """If possible, parses a `Reference` `uri` into more structured fields.

  This is only possible for two forms of reference uris:
  * Relative references of the form $TYPE/$ID, e.g., "Patient/1234"
    In this case, this will be parsed to a proto of the form:
    {patient_id: {value: "1234"}}
  * Fragments of the form "#$FRAGMENT", e.g., "#vs1".  In this case, this would
    be parsed into a proto of the form:
    {fragment: {value: "vs1"} }

  If the reference URI matches one of these schemas, the `uri` field will be
  cleared, and the appropriate structured fields set. Otherwise, the reference
  will be unchanged.

  Args:
    reference: The FHIR reference to potentially split.

  Raises:
    ValueError: If the message is not a valid FHIR Reference proto.
  """
  _validate_reference(reference)
  uri_field = reference.DESCRIPTOR.fields_by_name.get('uri')
  if not proto_utils.field_is_set(reference, uri_field):
    return  # No URI to split

  uri = proto_utils.get_value_at_field(reference, uri_field)

  internal_match = re.fullmatch(_INTERNAL_REFERENCE_PATTERN, uri.value)
  if internal_match is not None:
    # Note that we make the reference_id off of the reference before adding it,
    # since adding the reference_id would destroy the uri field, as they are
    # both in the same oneof. This allows us to copy fields from uri to
    # reference_id without making an extra copy.
    reference_id_field = get_reference_id_field_for_resource(
        reference, internal_match.group('resource_type'))
    reference_id = proto_utils.create_message_from_descriptor(
        reference_id_field.message_type)
    populate_typed_reference_id(reference_id,
                                internal_match.group('resource_id'),
                                internal_match.group('version'))

    proto_utils.copy_common_field(uri, reference_id, 'id')
    proto_utils.copy_common_field(uri, reference_id, 'extension')
    proto_utils.set_value_at_field(reference, reference_id_field, reference_id)
    return

  fragment_match = re.fullmatch(_FRAGMENT_REFERENCE_PATTERN, uri.value)
  if fragment_match is not None:
    # Note that we make the fragment off of the reference before adding it,
    # since adding the fragment would destroy the uri field, as they are both in
    # the same oneof. This allows us to copy fields from uri to fragment without
    # making an extra copy.
    fragment_field = reference.DESCRIPTOR.fields_by_name['fragment']
    fragment = proto_utils.create_message_from_descriptor(
        fragment_field.message_type)

    value_field = fragment.DESCRIPTOR.fields_by_name['value']
    proto_utils.set_value_at_field(fragment, value_field, uri.value[1:])
    proto_utils.copy_common_field(uri, fragment, 'id')
    proto_utils.copy_common_field(uri, fragment, 'extension')
    proto_utils.set_value_at_field(reference, fragment_field, fragment)
    return