Пример #1
0
def get_value_for_annotation_extension(
        message_or_descriptor: proto_utils.MessageOrDescriptorBase,
        extension_field: descriptor.FieldDescriptor) -> Optional[Any]:
    """Returns the value associated with the annotation extension field.

  Args:
    message_or_descriptor: A protobuf DescriptorProto, Message, or
      DescriptorBase to examine.
    extension_field: A FieldDescriptor describing the annotation whose value to
      retrieve.

  Returns:
    The value associated with extension_field, if one exists. Otherwise returns
    None.

  Raises:
    ValueError: Unable to retrieve Options for type: <type>.
  """
    options = get_options(message_or_descriptor)
    if proto_utils.field_is_repeated(extension_field):
        return options.Extensions[extension_field]

    if options.HasExtension(extension_field):
        return options.Extensions[extension_field]
    return None
Пример #2
0
 def _print_message_field(self, field_name: str,
                          field: descriptor.FieldDescriptor, value: Any):
     """Prints singular and repeated fields from a message."""
     self.generator.add_field(field_name)
     if proto_utils.field_is_repeated(field):
         self._print_list(cast(List[Any], value), self._print)
     else:
         self._print(cast(message.Message, value))
Пример #3
0
    def _print_primitive_field(self, field_name: str,
                               field: descriptor.FieldDescriptor,
                               value: Any) -> None:
        """Prints the primitive field.

    Args:
      field_name: The name of the field.
      field: The FielDescriptor whose contents to print.
      value: The value present at field to print.
    """
        if proto_utils.field_is_repeated(field):
            string_values = []
            elements = []
            extensions_found = False
            nonnull_values_found = False
            for primitive in value:
                wrapper = self.primitive_handler.primitive_wrapper_from_primitive(
                    primitive)
                string_values.append(wrapper.json_value())
                elements.append(wrapper.get_element())
                nonnull_values_found = nonnull_values_found or wrapper.has_value(
                )
                extensions_found = extensions_found or wrapper.has_element()

            # print string primitive representations
            if nonnull_values_found:
                self.generator.add_field(field_name)
                self._print_list(string_values, self.generator.push)

            # print Element representations
            if extensions_found:
                if nonnull_values_found:
                    self.generator.push(',')
                    self.generator.add_newline()
                self.generator.add_field(f'_{field_name}')
                self._print_list(elements, self._print)
        else:  # Singular field
            # TODO: Detect ReferenceId using an annotation
            if (self.json_format == _FhirJsonFormat.ANALYTIC
                    and field.message_type.name == 'ReferenceId'):
                str_value = proto_utils.get_value_at_field(value, 'value')
                self.generator.add_field(field_name, f'"{str_value}"')
            else:  # Wrap and print primitive value and (optionally), its element
                wrapper = self.primitive_handler.primitive_wrapper_from_primitive(
                    value)
                if wrapper.has_value():
                    self.generator.add_field(field_name, wrapper.json_value())
                if wrapper.has_element(
                ) and self.json_format == _FhirJsonFormat.PURE:
                    if wrapper.has_value():
                        self.generator.push(',')
                        self.generator.add_newline()
                    self.generator.add_field(f'_{field_name}')
                    self._print(wrapper.get_element())
Пример #4
0
def add_message_to_extension(msg: message.Message,
                             extension: message.Message) -> None:
    """Adds the contents of msg to extension.

  Args:
    msg: A FHIR profile of Extension, whose contents should be added to the
      generic extension.
    extension: The generic Extension to populate.
  """
    if not fhir_types.is_profile_of_extension(msg):
        raise ValueError(f'Message: {msg.DESCRIPTOR.full_name} is not a valid '
                         'FHIR Extension profile.')

    if not fhir_types.is_extension(extension):
        raise ValueError(
            f'Extension: {extension.DESCRIPTOR.full_name} is not a '
            'valid FHIR Extension.')

    cast(Any,
         extension).url.value = annotation_utils.get_structure_definition_url(
             msg.DESCRIPTOR)

    # Copy over the id field if present
    if proto_utils.field_is_set(msg, 'id'):
        proto_utils.copy_common_field(msg, extension, 'id')

    # Copy the vlaue fields from message into the extension
    value_fields = [
        field for field in msg.DESCRIPTOR.fields
        if field.name not in NON_VALUE_FIELDS
    ]
    if not value_fields:
        raise ValueError(
            f'Extension has no value fields: {msg.DESCRIPTOR.name}.')

    # Add fields to the extension. If there is a single value field, a simple
    # value assignment will suffice. Otherwise, we need to loop over all fields
    # and add them as child extensions
    if (len(value_fields) == 1
            and not proto_utils.field_is_repeated(value_fields[0])):
        value_field = value_fields[0]
        _verify_field_is_proto_message_type(value_field)
        if proto_utils.field_is_set(msg, value_field):
            value = proto_utils.get_value_at_field(msg, value_field)
            _add_value_to_extension(
                value, extension,
                annotation_utils.is_choice_type_field(value_field))
        else:
            # TODO: Invalid FHIR; throw an error here?
            pass
    else:  # Add child extensions...
        _add_fields_to_extension(msg, extension)
Пример #5
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)
Пример #6
0
def add_extension_to_message(extension: message.Message, msg: message.Message):
    """Recursively parses extension and adds to message.

  Args:
    extension: The FHIR extension to serialize and add.
    msg: The message to add the extension onto

  Raises:
    InvalidFhirError: In the event that a value is set on the extension, but the
    corresponding message field to copy it to is repeated (extension values are
    singular only).
  """
    desc = msg.DESCRIPTOR
    fields_by_url = {
        get_inlined_extension_url(field): field
        for field in desc.fields if field.name != 'id'
    }

    # Copy the id field if present
    id_field = desc.fields_by_name.get('id')
    if proto_utils.field_is_set(extension, id_field):
        proto_utils.set_value_at_field(msg, id_field, cast(Any, extension).id)

    # Handle simple extensions (only one value present)
    if proto_utils.field_is_set(extension, 'value'):
        if len(fields_by_url) != 1:
            raise fhir_errors.InvalidFhirError(
                f'Expected a single field, found {len(fields_by_url)}; '
                f'{desc.full_name} is an invalid extension type.')

        field = list(fields_by_url.items())[0][1]
        if proto_utils.field_is_repeated(field):
            raise fhir_errors.InvalidFhirError(
                f'Expected {field.full_name} to be a singular field. '
                f'{desc.full_name} is an invalid extension type.')
        _add_extension_value_to_message(extension, msg, field)
        return

    # Else, iterate through all child extensions...
    child_extensions = proto_utils.get_value_at_field(extension, 'extension')
    for child_extension in child_extensions:
        field = fields_by_url.get(child_extension.url.value)
        if field is None:
            raise ValueError(f'Message of type: {desc.full_name} has no field '
                             f'with name: {child_extension.url.value}.')

        # Simple value type on child_extension...
        if proto_utils.field_is_set(child_extension, 'value'):
            _add_extension_value_to_message(child_extension, msg, field)
            continue

        # Recurse for nested composite messages...
        if not proto_utils.field_is_repeated(field):
            if proto_utils.field_is_set(msg, field):
                raise ValueError(
                    f'Field: {field.full_name} is already set on message: '
                    f'{desc.full_name}.')

            if proto_utils.field_content_length(child_extension,
                                                'extension') > 1:
                raise ValueError(
                    f'Cardinality mismatch between field: {field.full_name} and '
                    f'extension: {desc.full_name}.')

        child_message = proto_utils.set_in_parent_or_add(msg, field)
        add_extension_to_message(child_extension, child_message)
Пример #7
0
def _add_extension_value_to_message(extension: message.Message,
                                    msg: message.Message,
                                    message_field: descriptor.FieldDescriptor):
    """Serialize the provided extension and add it to the message.

  Args:
    extension: The FHIR extension to serialize.
    msg: The message to add the serialized extension to.
    message_field: The field on the message to set.

  Raises:
    InvalidFhirError: In the event that the field to be set is not a singular
    message type, or if the provided extension is not singular (has nested
    extensions).
  """
    if message_field.type != descriptor.FieldDescriptor.TYPE_MESSAGE:
        raise fhir_errors.InvalidFhirError(
            f'{msg.DESCRIPTOR.full_name} is not a FHIR extension type.')

    extension_field = extension.DESCRIPTOR.fields_by_name['extension']
    if proto_utils.field_content_length(extension, extension_field) > 0:
        raise fhir_errors.InvalidFhirError(
            'No child extensions should be set on '
            f'{extension.DESCRIPTOR.full_name}.')

    value_field = _get_populated_extension_value_field(extension)

    # If a choice type, need to assign the extension value to the correct field.
    if annotation_utils.is_choice_type_field(message_field):
        choice_message = proto_utils.get_value_at_field(msg, message_field)
        choice_descriptor = choice_message.DESCRIPTOR

        for choice_field in choice_descriptor.fields:
            if (value_field.message_type.full_name ==
                    choice_field.message_type.full_name):
                _add_extension_value_to_message(extension, choice_message,
                                                choice_field)
                return

        raise ValueError(
            f'No field on Choice Type {choice_descriptor.full_name} '
            f'for extension {extension.DESCRIPTOR.full_name}.')

    # If the target message is a bound Code type, we need to convert the generic
    # Code field from the extension into the target typed Code.
    if annotation_utils.has_fhir_valueset_url(message_field.message_type):
        typed_code = proto_utils.set_in_parent_or_add(msg, message_field)
        codes.copy_code(cast(Any, extension).value.code, typed_code)
        return

    # If the target message is bound to a Coding type, we must convert the generic
    # Coding field from the extension into the target typed Coding.
    if fhir_types.is_type_or_profile_of_coding(message_field.message_type):
        typed_coding = proto_utils.set_in_parent_or_add(msg, message_field)
        codes.copy_coding(cast(Any, extension).value.coding, typed_coding)
        return

    # Value types must match
    if not proto_utils.are_same_message_type(value_field.message_type,
                                             message_field.message_type):
        raise ValueError(
            'Missing expected value of type '
            f'{message_field.message_type.full_name} in extension '
            f'{extension.DESCRIPTOR.full_name}.')

    value = proto_utils.get_value_at_field(
        cast(Any, extension).value, value_field)
    if proto_utils.field_is_repeated(message_field):
        proto_utils.append_value_at_field(msg, message_field, value)
    else:
        proto_utils.set_value_at_field(msg, message_field, value)