Beispiel #1
0
    def _print_choice_field(self, field_name: str,
                            field: descriptor.FieldDescriptor,
                            choice_container: message.Message) -> None:
        """Prints a FHIR choice field.

    This field is expected to have one valid oneof set.

    Args:
      field_name: The name of the field.
      field: The FieldDescriptor whose contents to print.
      choice_container: The value present at field, which should be a oneof with
        a single value set.
    """
        if len(choice_container.DESCRIPTOR.oneofs) != 1:
            raise ValueError(f'Invalid value for choice field {field_name}: '
                             f'{choice_container}.')
        oneof_group = choice_container.DESCRIPTOR.oneofs[0]
        set_oneof_name = choice_container.WhichOneof(oneof_group.name)
        if set_oneof_name is None:
            raise ValueError('Oneof not set on choice type: '
                             f'{choice_container.DESCRIPTOR.full_name}.')
        value_field = choice_container.DESCRIPTOR.fields_by_name[
            set_oneof_name]
        oneof_field_name = value_field.json_name
        oneof_field_name = oneof_field_name[0].upper() + oneof_field_name[1:]

        value = proto_utils.get_value_at_field(choice_container, value_field)
        if annotation_utils.is_primitive_type(value_field.message_type):
            self._print_primitive_field(field_name + oneof_field_name,
                                        value_field, value)
        else:
            self._print_message_field(field_name + oneof_field_name,
                                      value_field, value)
Beispiel #2
0
def reference_to_string(reference: message.Message) -> str:
    """Returns a reference URI for a typed reference message."""
    _validate_reference(reference)

    # Early-exit if URI or fragment is set
    if proto_utils.field_is_set(reference, 'uri'):
        uri = proto_utils.get_value_at_field(reference, 'uri')
        return uri.value
    elif proto_utils.field_is_set(reference, 'fragment'):
        fragment = proto_utils.get_value_at_field(reference, 'fragment')
        return f'#{fragment.value}'

    set_oneof = reference.WhichOneof('reference')
    if set_oneof is None:
        raise ValueError(
            f'Reference is not set on: {reference.DESCRIPTOR.name}.')

    # Convert to CamelCase
    prefix = path_utils.snake_case_to_camel_case(set_oneof, upper=True)

    # Cull trailing 'Id'
    if prefix.endswith('Id'):
        prefix = prefix[:-2]

    reference_id = proto_utils.get_value_at_field(reference, set_oneof)
    reference_string = f'{prefix}/{reference_id.value}'
    if proto_utils.field_is_set(reference_id, 'history'):
        reference_string += f'/_history/{reference_id.history.value}'
    return reference_string
Beispiel #3
0
def _add_value_to_extension(msg: message.Message, extension: message.Message,
                            is_choice_type: bool):
    """Adds the fields from msg to a generic Extension.

  Attempts are first made to set the "value" field of the generic Extension
  based on the type of field set on message. If this fails, checks are made
  against the generic Code and Coding types, and finally we fall back to adding
  the message's fields as sub-extensions.

  Args:
    msg: The message whose values to add to extension.
    extension: The generic Extension to populate.
    is_choice_type: Whether or not the provided message represents a "choice"
    type.
  """
    if is_choice_type:
        oneofs = msg.DESCRIPTOR.oneofs
        if not oneofs:
            raise fhir_errors.InvalidFhirError(
                f'Choice type is missing a oneof: {msg.DESCRIPTOR.full_name}')
        value_field_name = msg.WhichOneof(oneofs[0].name)
        if value_field_name is None:
            raise ValueError('Choice type has no value set: '
                             f'{msg.DESCRIPTOR.full_name}')
        value_field = msg.DESCRIPTOR.fields_by_name[value_field_name]
        _verify_field_is_proto_message_type(value_field)
        _add_value_to_extension(
            proto_utils.get_value_at_field(msg, value_field), extension, False)
    else:
        # Try to set the message directly as a datatype value on the extension.
        # E.g., put the message of type Boolean into the value.boolean field
        value_field_mapping = _get_value_field_mapping_for_extension(extension)
        value_field = value_field_mapping.get(msg.DESCRIPTOR.full_name)
        if value_field is not None:
            proto_utils.set_value_at_field(
                cast(Any, extension).value, value_field, msg)
        elif annotation_utils.has_fhir_valueset_url(msg):
            codes.copy_code(msg, cast(Any, extension).value.code)
        elif fhir_types.is_type_or_profile_of_coding(msg):
            codes.copy_coding(msg, cast(Any, extension).value.coding)
        else:  # Fall back to adding individual fields as sub-extensions
            _add_fields_to_extension(msg, extension)
Beispiel #4
0
    def _print_reference(self, reference: message.Message):
        """Standardizes and prints the provided reference.

    Note that "standardization" in this case refers to un-typing the typed-
    reference prior to printing.

    Args:
      reference: The reference to print.
    """
        set_oneof = reference.WhichOneof('reference')
        if set_oneof is None or set_oneof == 'uri':  # Reference is already standard
            self._print_message(reference)
        else:
            new_reference = copy.copy(reference)

            # Setting the new URI field will overwrite the oneof
            new_uri = proto_utils.get_value_at_field(new_reference, 'uri')
            proto_utils.set_value_at_field(
                new_uri, 'value', references.reference_to_string(reference))
            self._print_message(new_reference)
Beispiel #5
0
def _validate_fhir_constraints(
        msg: message.Message, base_name: str,
        primitive_handler_: primitive_handler.PrimitiveHandler):
    """Iterates over fields of the provided message and validates constraints.

  Args:
    msg: The message to validate.
    base_name: The root message name for recursive validation of nested message
      fields.
    primitive_handler_: Responsible for returning PrimitiveWrappers.

  Raises:
    fhir_errors.InvalidFhirError: In the event that a field is found to be
    violating FHIR constraints or a required oneof is not set.
  """
    if annotation_utils.is_primitive_type(msg):
        # Validation is implicitly done on the primitive type during wrapping
        _ = primitive_handler_.primitive_wrapper_from_primitive(msg)
        return

    if proto_utils.is_message_type(msg, any_pb2.Any):
        # We do not validate "Any" constrained resources.
        # TODO: Potentially unpack the correct type and validate?
        return

    # Enumerate and validate fields of the message
    for field in msg.DESCRIPTOR.fields:
        field_name = f'{base_name}.{field.json_name}'
        _validate_field(msg, field, field_name, primitive_handler_)

    # Also verify that oneof fields are set. Note that optional choice-types
    # should have the containing message unset - if the containing message is set,
    # it should have a value set as well
    for oneof in msg.DESCRIPTOR.oneofs:
        if (msg.WhichOneof(oneof.name) is None
                and not oneof.GetOptions().HasExtension(
                    annotations_pb2.fhir_oneof_is_optional)):
            raise fhir_errors.InvalidFhirError(
                f'Empty oneof: `{oneof.full_name}`.')
Beispiel #6
0
def get_contained_resource(
        contained_resource: message.Message) -> message.Message:
    """Returns the resource instance contained within `contained_resource`.

  Args:
    contained_resource: The containing `ContainedResource` instance.

  Returns:
    The resource contained by `contained_resource`.

  Raises:
    TypeError: In the event that `contained_resource` is not of type
    `ContainedResource`.
    ValueError: In the event that the oneof on `contained_resource` is not set.
  """
    # TODO: Use an annotation here.
    if contained_resource.DESCRIPTOR.name != 'ContainedResource':
        raise TypeError('Expected `ContainedResource` but got: '
                        f'{type(contained_resource)}.')
    oneof_field = contained_resource.WhichOneof('oneof_resource')
    if oneof_field is None:
        raise ValueError('`ContainedResource` oneof not set.')
    return proto_utils.get_value_at_field(contained_resource, oneof_field)
Beispiel #7
0
    def _print_reference(self, reference: message.Message) -> None:
        """Standardizes and prints the provided reference.

    Note that "standardization" in the case of PURE FHIR JSON refers to
    un-typing the typed-reference prior to printing.

    Args:
      reference: The reference to print.
    """
        set_oneof = reference.WhichOneof('reference')
        if (self.json_format == _FhirJsonFormat.PURE and set_oneof is not None
                and set_oneof != 'uri'):
            # In pure FHIR mode, we have to serialize structured references
            # into FHIR uri strings.
            standardized_reference = copy.copy(reference)

            # Setting the new URI field will overwrite the original oneof
            new_uri = proto_utils.get_value_at_field(standardized_reference,
                                                     'uri')
            proto_utils.set_value_at_field(
                new_uri, 'value', references.reference_to_string(reference))
            self._print_message(standardized_reference)
        else:
            self._print_message(reference)