def validate_primitive_without_value(fhir_primitive: message.Message):
    """Validates a Message which has the PrimitiveWithoutValue extension.

  Given that there is a PrimitiveWithoutValue extension present, there must be
  at least one other extension. Otherwise, there is truly no value set other
  than id and/or extension (non-value fields).

  Args:
    fhir_primitive: The FHIR primitive Message to validate.

  Raises:
    fhir_errors.InvalidFhirError: In the event that there is less than one
    extension present, or there are values set other than id and/or extension.
  """
    name = fhir_primitive.DESCRIPTOR.full_name

    # There must be at least one other FHIR extension
    if len(extensions.get_fhir_extensions(fhir_primitive)) < 2:
        raise fhir_errors.InvalidFhirError(
            f'{name!r} must have either extensions or a value present.')

    # ... Else truly no value set other than id and/or extension
    for field, _ in fhir_primitive.ListFields():  # Iterate set fields only
        if field.name not in extensions.NON_VALUE_FIELDS:
            raise fhir_errors.InvalidFhirError(
                f'{name!r} contains PrimitiveHasNoValue but {field.name!r} is set.'
            )
Exemple #2
0
    def _print_message(self, msg: message.Message) -> None:
        """Prints the representation of message."""
        self.generator.open_json_object()

        # Add the resource type preamble if necessary
        set_fields = msg.ListFields()
        if (annotation_utils.is_resource(msg)
                and self.json_format == _FhirJsonFormat.PURE):
            self.generator.add_field('resourceType',
                                     f'"{msg.DESCRIPTOR.name}"')
            if set_fields:
                self.generator.push(',')
                self.generator.add_newline()

        # Print all fields
        for (i, (set_field, value)) in enumerate(set_fields):
            if (annotation_utils.is_choice_type_field(set_field)
                    and self.json_format == _FhirJsonFormat.PURE):
                self._print_choice_field(set_field.json_name, set_field, value)
            elif annotation_utils.is_primitive_type(set_field.message_type):
                self._print_primitive_field(set_field.json_name, set_field,
                                            value)
            else:
                self._print_message_field(set_field.json_name, set_field,
                                          value)

            if i < (len(set_fields) - 1):
                self.generator.push(',')
                self.generator.add_newline()

        self.generator.close_json_object()
Exemple #3
0
    def _GetMethodUrlAndPathParamsNames(
        self,
        handler_name: str,
        args: message.Message,
    ) -> Tuple[reflection_pb2.ApiMethod, str, Iterable[str]]:
        path_params = {}  # Dict[str, Union[int, str]]
        if args:
            for field, value in args.ListFields():
                if self.handlers_map.is_endpoint_expecting(
                        handler_name, field.name):
                    path_params[
                        field.name] = self._CoerceValueToQueryStringType(
                            field, value)

        url = self.urls.build(handler_name, path_params, force_external=True)

        method = None
        for rule in self.handlers_map.iter_rules():
            if rule.endpoint == handler_name:
                method = [m for m in rule.methods if m != "HEAD"][0]

        if not method:
            raise RuntimeError("Can't find method for %s" % handler_name)

        return method, url, list(path_params.keys())
Exemple #4
0
def collect_formatted_strings(
        proto: message.Message,
        field_modifier: Callable[[str, str, 'options_pb2.StringFormat.V'], str] =
        lambda s, p, f: s,
        path: Tuple[str, ...] = ()) \
        -> Iterator[Tuple[str, str, 'options_pb2.StringFormat.V']]:
    """Iterate recursively through all fields of a proto message to find fields with
    specific string formats.

    Args:
    - proto, the proto message from which to collect strings
    - field_modifier, a function to modify fields in place in the proto:
        it is given a string, its path in the proto and its string_format, and should produce
        another string. If a field in the proto has several string_formats, the modifier is applied
        once for each string_format, in the order in which they were declared. Default is no-op.
    - path, a tuple to prepend to all output paths (needed for recursion).
    Yields:
    Triples (collected_string, path_in_object, string_format):
    - collected_string, a string found in a (possibly repeated) field in the proto
    - path, the path where the string can be found in the proto, as a dot-separated string. For
        repeated fields, the index is provided.
    - string_format an options_pb2.StringFormat value that is declared on this field.
    """

    for field_descriptor, value in proto.ListFields():
        next_path = path + (field_descriptor.name, )
        this_string_formats = \
            field_descriptor.GetOptions().Extensions[options_pb2.string_format]
        if field_descriptor.type == field_descriptor.TYPE_MESSAGE:
            if field_descriptor.label == field_descriptor.LABEL_REPEATED:
                try:
                    value.items()
                    # TODO(cyrille): Deal with map types.
                    logging.warning('Ignoring a map field: %s',
                                    '.'.join(next_path))
                    continue
                except TypeError:
                    pass
                for index, repeated_value in enumerate(value):
                    yield from collect_formatted_strings(
                        repeated_value, field_modifier,
                        next_path + (str(index), ))
            else:
                yield from collect_formatted_strings(value, field_modifier,
                                                     next_path)
            continue
        for string_format in this_string_formats:
            if field_descriptor.label == field_descriptor.LABEL_REPEATED:
                for index, repeated_value in enumerate(value):
                    to_yield = repeated_value, '.'.join(
                        next_path + (str(index), )), string_format
                    value[index] = field_modifier(*to_yield)
                    yield to_yield
            else:
                to_yield = value, '.'.join(next_path), string_format
                updated_value = field_modifier(*to_yield)
                setattr(proto, field_descriptor.name, updated_value)
                yield to_yield
                # Pass the updated value to the next iteration of the this_string_formats loop.
                value = updated_value
Exemple #5
0
def _iter_sensitive_fields(msg: message.Message, field_usages_to_yield: Set[int]) \
        -> Iterator[Tuple[message.Message, descriptor.FieldDescriptor]]:
    """Iterate recursively through all fields of a proto message to find sensitive fields."""

    for field_descriptor, value in msg.ListFields():
        extensions = field_descriptor.GetOptions().Extensions
        field_usage = extensions[options_pb2.field_usage]
        if field_usage in field_usages_to_yield:
            yield msg, field_descriptor
            continue

        # Timestamp are always considered as sensitive.
        if field_descriptor.message_type == timestamp_pb2.Timestamp.DESCRIPTOR:
            yield msg, field_descriptor
            continue

        if field_descriptor.type == field_descriptor.TYPE_MESSAGE:
            if hasattr(value, 'DESCRIPTOR'):
                for field in _iter_sensitive_fields(value,
                                                    field_usages_to_yield):
                    yield field
            else:
                for repeated_value in value:
                    if not hasattr(repeated_value, 'DESCRIPTOR'):
                        # value is actually a ScalarMap.
                        break
                    for field in _iter_sensitive_fields(
                            repeated_value, field_usages_to_yield):
                        yield field
def substitute_runtime_parameter(
        msg: message.Message,
        parameter_bindings: Mapping[str, types.Property]) -> None:
    """Utility function to substitute runtime parameter placeholders with values.

  Args:
    msg: The original message to change. Only messages defined under
      pipeline_pb2 will be supported. Other types will result in no-op.
    parameter_bindings: A dict of parameter keys to parameter values that will
      be used to substitute the runtime parameter placeholder.

  Returns:
    None
  """
    if not isinstance(msg, message.Message):
        return

    # If the message is a pipeline_pb2.Value instance, try to find an substitute
    # with runtime parameter bindings.
    if isinstance(msg, pipeline_pb2.Value):
        value = cast(pipeline_pb2.Value, msg)
        which = value.WhichOneof('value')
        if which == 'runtime_parameter':
            real_value = _get_runtime_parameter_value(value.runtime_parameter,
                                                      parameter_bindings)
            if real_value is None:
                return
            value.Clear()
            common_utils.set_metadata_value(metadata_value=value.field_value,
                                            value=real_value)
        if which == 'structural_runtime_parameter':
            real_value = _get_structural_runtime_parameter_value(
                value.structural_runtime_parameter, parameter_bindings)
            if real_value is None:
                return
            value.Clear()
            common_utils.set_metadata_value(metadata_value=value.field_value,
                                            value=real_value)

        return

    # For other cases, recursively call into sub-messages if any.
    for field, sub_message in msg.ListFields():
        # No-op for non-message types.
        if field.type != descriptor.FieldDescriptor.TYPE_MESSAGE:
            continue
        # Evaluates every map values in a map.
        elif (field.message_type.has_options
              and field.message_type.GetOptions().map_entry):
            for key in sub_message:
                substitute_runtime_parameter(sub_message[key],
                                             parameter_bindings)
        # Evaluates every entry in a list.
        elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
            for element in sub_message:
                substitute_runtime_parameter(element, parameter_bindings)
        # Evaluates sub-message.
        else:
            substitute_runtime_parameter(sub_message, parameter_bindings)
Exemple #7
0
 def Recurse(msg: message.Message, prev: Tuple[str, ...]) -> None:
     fields = sorted(msg.ListFields(), key=lambda field: field[0].name)
     for field, value in fields:
         curr = prev + (field.name, )
         if field.type == descriptor.FieldDescriptor.TYPE_MESSAGE:
             Recurse(value, curr)
         else:
             result[".".join(curr)] = transform(field, value)
def which_has(msg: Message) -> str:
    """
    Like WhichOneof, but for our fake oneofs using "has_" fields.
    Returns the non-"has_" field name which is set on `msg`.
    """
    # TODO remove this once we can use oneofs in our protos
    for field_descriptor, value in msg.ListFields():
        if not field_descriptor.name.startswith("has_"):
            continue
        if value is True:
            return field_descriptor.name[4:]
    raise ValueError(
        "Invalid {} protobuf: none of the has_ values are set.".format(
            type(msg).__name__
        )
    )
Exemple #9
0
    def _print_contained_resource(self, contained_resource: message.Message):
        """Prints the set fields of the contained resource.

    If the _FhirJsonFormat is set to ANALYTIC, this method only prints the url.

    Args:
      contained_resource: The contained resource to iterate over and print.
    """
        for (_, set_field_value) in contained_resource.ListFields():
            if self.json_format == _FhirJsonFormat.ANALYTIC:
                structure_definition_url = (
                    annotation_utils.get_structure_definition_url(
                        set_field_value))
                self.generator.push(f'"{structure_definition_url}"')
            else:  # print the entire contained resource...
                self._print(set_field_value)
Exemple #10
0
 def Canonicalize(message: Message):
     for (fd, v) in message.ListFields():
         if fd.label != fd.LABEL_REPEATED:
             continue
         # Order of arguments is fixed.
         if fd.name == 'arguments':
             continue
         for item in v:
             if isinstance(item, Message):
                 Canonicalize(item)
         if len(v) == 0:
             continue
         if isinstance(v[0], Message):
             v.sort(key=NameOrSelf)
         else:
             v.sort()
Exemple #11
0
def encode_message(
        msg: Message,
        parents: List[FieldDescriptor] = []) -> Generator[bytes, None, None]:
    msg_path = [p.number for p in parents]
    for fd, val in msg.ListFields():
        if fd.type == FieldDescriptor.TYPE_MESSAGE:
            for d in encode_message(val, parents + [fd]):
                yield d
        elif fd.type == FieldDescriptor.TYPE_GROUP:
            raise NotImplementedError()
        else:
            path = msg_path + [fd.number]
            if fd.label == FieldDescriptor.LABEL_REPEATED:
                for i, v in enumerate(val):
                    yield encode_scalar(path + [i], fd, v)
            else:
                yield encode_scalar(path, fd, val)
Exemple #12
0
def from_message(msg: message.Message,
                 components: Optional[List[Text]] = None) -> pd.DataFrame:
  """Converts protobuf message to a dataframe.

  Args:
    msg: Protobuf message to convert.
    components: Prefixes for column names.

  Returns:
    Pandas dataframe representing given message.
  """
  if components is None:
    components = []

  data = {}
  for desc, value in msg.ListFields():
    if isinstance(value, message.Message):
      data.update(from_message(value, components + [desc.name]))
    else:
      data.update(_get_pretty_value(value, desc, components))

  return pd.DataFrame(data=data)
Exemple #13
0
def _(message: Message) -> dict:
    exprs = getattr(message, 'exprs', None)
    if exprs is not None:
        return [expr_to_obj(expr) for expr in to_dict(exprs)]
    return {f[0].name: to_dict(f[1]) for f in message.ListFields()}