示例#1
0
  def testGetValueAtFieldIndex_invalidIndex_raisesException(self):
    """Test get_value_at_field_index with an invalid index."""
    patient = patient_pb2.Patient(active=datatypes_pb2.Boolean(value=True))
    with self.assertRaises(ValueError) as ve:
      proto_utils.get_value_at_field_index(patient, "active", 1)

    self.assertIsInstance(ve.exception, ValueError)
示例#2
0
def _validate_field(msg: message.Message, field: descriptor.FieldDescriptor,
                    field_name: str,
                    primitive_handler_: primitive_handler.PrimitiveHandler):
    """Validates that required fields are set, and performs basic temporal checks.

  Args:
    msg: The Message that the field belongs to.
    field: The FieldDescriptor of the field to examine.
    field_name: The name of the field.
    primitive_handler_: Responsible for returning PrimitiveWrappers.

  Raises:
    fhir_errors.InvalidFhirError: In the event that a required field is not set
    or if temporal requirements are not met.
  """
    if annotation_utils.field_is_required(
            field) and not proto_utils.field_is_set(msg, field):
        raise fhir_errors.InvalidFhirError(
            f'Required field `{field.full_name}` is missing.')

    if annotation_utils.is_reference(field.message_type):
        _validate_reference_field(msg, field)
        return

    if field.type == descriptor.FieldDescriptor.TYPE_MESSAGE:
        # Returns the first value only for a singular field
        for i in range(proto_utils.field_content_length(msg, field)):
            submessage = proto_utils.get_value_at_field_index(msg, field, i)
            _validate_fhir_constraints(submessage, field_name,
                                       primitive_handler_)

            # Run extra validation for some types, until FHIRPath validation covers
            # these as well
            if fhir_types.is_period(submessage):
                _validate_period(submessage, field_name)
示例#3
0
def _validate_reference_field(parent: message.Message,
                              field: descriptor.FieldDescriptor):
    """Ensure that the provided reference field is valid.

  Args:
    parent: The containing Message.
    field: The reference field descriptor.

  Raises:
    fhir_errors.InvalidFhirError: In the event of an empty reference (no
    extensions, no identifier, no display).
  """
    oneof = field.message_type.oneofs[0]

    # Singular fields have a length of 1
    for i in range(proto_utils.field_content_length(parent, field)):
        reference = proto_utils.get_value_at_field_index(parent, field, i)
        reference_field_name = reference.WhichOneof(oneof.name)

        if reference_field_name is None:
            if not (reference.extension or reference.HasField('identifier')
                    or reference.HasField('display')):
                raise fhir_errors.InvalidFhirError(
                    f'`{reference.DESCRIPTOR.name}` is an empty reference.')
            # There's no reference field, but there is other data. This is valid.
            return

        field_options = field.GetOptions()
        if not field_options.Extensions[annotations_pb2.valid_reference_type]:
            # The reference field does not have restrictions, so any value is fine.
            return

        if reference.HasField('uri') or reference.HasField('fragment'):
            # Uri and Fragment references are untyped.
            return

        # There are no reference annotations for DSTU2; skip validation
        if annotation_utils.get_fhir_version(
                reference) == annotations_pb2.FhirVersion.DSTU2:
            return

        reference_field = reference.DESCRIPTOR.fields_by_name[
            reference_field_name]
        if annotation_utils.is_typed_reference_field(reference_field):
            # Ensure that the reference type is listed as "valid"
            reference_type = reference_field.GetOptions().Extensions[
                annotations_pb2.referenced_fhir_type]
            is_allowed = False
            for valid_type in field_options.Extensions[
                    annotations_pb2.valid_reference_type]:
                if valid_type == reference_type or valid_type == 'Resource':
                    is_allowed = True
                    break

            if not is_allowed:
                raise fhir_errors.InvalidFhirError(
                    f'Message `{parent.DESCRIPTOR.full_name}` contains an invalid '
                    f'reference type: `{reference_type}` set at: '
                    f'`{reference_field_name}`.')
示例#4
0
def _add_fields_to_extension(msg: message.Message, extension: message.Message):
    """Adds the fields from message to extension."""
    for field in msg.DESCRIPTOR.fields:
        _verify_field_is_proto_message_type(field)

        # Add submessages to nested extensions; singular fields have a length of 1
        for i in range(proto_utils.field_content_length(msg, field)):
            child_extension = proto_utils.set_in_parent_or_add(
                extension, 'extension')
            cast(Any,
                 child_extension).url.value = get_inlined_extension_url(field)
            value = proto_utils.get_value_at_field_index(msg, field, i)
            _add_value_to_extension(
                value, child_extension,
                annotation_utils.is_choice_type_field(field))
示例#5
0
 def testGetValueAtFieldIndex_withSingularField_returnsValue(self):
   """Test get_value_at_field_index with a singular field."""
   patient = patient_pb2.Patient(active=datatypes_pb2.Boolean(value=True))
   result = proto_utils.get_value_at_field_index(patient, "active", 0)
   self.assertTrue(result.value)
示例#6
0
 def testGetValueAtFieldIndex_withRepeatedField_returnsValueAtIndex(self):
   """Test get_value_at_field_index with a repeated field."""
   patient = self._create_patient_with_names(["A", "B", "C"])
   result = proto_utils.get_value_at_field_index(patient, "name", 1)
   self.assertEqual(result.text.value, "B")
示例#7
0
    def _merge_field(self, json_value: Any, field: descriptor.FieldDescriptor,
                     parent: message.Message):
        """Merges the json_value into the provided field of the parent Message.

    Args:
      json_value: The JSON value to set.
      field: The FieldDescriptor of the field to set in parent.
      parent: The parent Message to set the value on.

    Raises:
      ValueError: In the event that a non-primitive field has already been set.
      ValueError: In the event that a oneof field has already been set.
    """
        # If the field is non-primitive, ensure that it hasn't been set yet. Note
        # that we allow primitive types to be set already, because FHIR represents
        # extensions to primitives as separate, subsequent JSON elements, with the
        # field prepended by an underscore
        if (not annotation_utils.is_primitive_type(field.message_type)
                and proto_utils.field_is_set(parent, field)):
            raise ValueError('Target field {} is already set.'.format(
                field.full_name))

        # Consider it an error if a oneof field is already set.
        #
        # Exception: When a primitive in a choice type has a value and an extension,
        # it will get set twice, once by the value (e.g. valueString) and once by an
        # extension (e.g., _valueString)
        if field.containing_oneof is not None:
            oneof_field = parent.DESCRIPTOR.oneofs_by_name[
                field.containing_oneof.name]
            if (annotation_utils.is_primitive_type(field.message_type)
                    and oneof_field.full_name == field.full_name):
                raise ValueError(
                    'Cannot set field {} since oneof field {} is already set.'.
                    format(field.full_name, oneof_field.full_name))

        # Ensure that repeated fields get proper list assignment
        existing_field_size = proto_utils.field_content_length(parent, field)
        if proto_utils.field_is_repeated(field):
            if not isinstance(json_value, list):
                raise ValueError(
                    f'Attempted to merge a repeated field, {field.name}, a json_value '
                    f'with type {type(json_value)} instead of a list.')

            if existing_field_size != 0 and existing_field_size != len(
                    json_value):
                raise ValueError(
                    'Repeated primitive list length does not match extension list for '
                    'field: {field.full_name!r}.')

        # Set the JSON values, taking care to clear the PRIMITIVE_HAS_NO_VALUE_URL
        # if we've already visited the field before.
        json_value = (json_value if proto_utils.field_is_repeated(field) else
                      [json_value])
        for (i, value) in enumerate(json_value):
            parsed_value = self._parse_field_value(field, value)
            if existing_field_size > 0:
                field_value = proto_utils.get_value_at_field_index(
                    parent, field, i)
                field_value.MergeFrom(parsed_value)
                extensions.clear_fhir_extensions_with_url(
                    field_value, extensions.PRIMITIVE_HAS_NO_VALUE_URL)
            else:
                field_value = proto_utils.set_in_parent_or_add(parent, field)
                field_value.MergeFrom(parsed_value)