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)
def testAppendValueAtField_singularCompositeValue_raises(self): """Test append_value_at_field with a singular composite type.""" patient = patient_pb2.Patient() active = datatypes_pb2.Boolean(value=True) with self.assertRaises(ValueError) as ve: proto_utils.append_value_at_field(patient, "active", active) self.assertIsInstance(ve.exception, ValueError)
def merge_value(self, json_value: Any, target: message.Message): """Merges the provided json_value into the target Message. Args: json_value: A Python-native representation of JSON data. target: The target Message to merge the JSON data into. """ target_descriptor = target.DESCRIPTOR if annotation_utils.is_primitive_type(target_descriptor): if isinstance(json_value, dict): # This is a primitive type extension. Merge the extension fields into # the empty target proto, and tag it as having no value. self._merge_message(json_value, target) extension_field = target_descriptor.fields_by_name.get( 'extension') if extension_field is None: raise ValueError( "Invalid primitive. No 'extension' field exists on " f"{target_descriptor.full_name}.") primitive_has_no_value = extensions.create_primitive_has_no_value( extension_field.message_type) proto_utils.append_value_at_field(target, extension_field, primitive_has_no_value) else: wrapper = self.primitive_handler.primitive_wrapper_from_json_value( json_value, type(target), default_timezone=self.default_timezone) wrapper.merge_into(target) elif annotation_utils.is_reference(target_descriptor): self._merge_message(json_value, target) references.split_if_relative_reference(target) else: if isinstance(json_value, dict): # The provided value must be another FHIR element self._merge_message(json_value, target) elif isinstance(json_value, (tuple, list)) and len(json_value) == 1: # Check if the target field is non-repeated, and we're trying to # populate it with a single-element array. This is considered valid, and # occurs when a profiled resource reduces the size of a repeated FHIR # field to a maximum of 1. self._merge_message(json_value[0], target) else: raise ValueError( 'Expected a JSON object for field of type: {}.'.format( target_descriptor.full_name))
def testAppendValueAtField_repeatedCompositeValue_appendsValue(self): """Test append_value_at_field with a repeated composite type.""" patient = patient_pb2.Patient() patient_names = [ datatypes_pb2.HumanName(text=datatypes_pb2.String(value="Foo")), datatypes_pb2.HumanName(text=datatypes_pb2.String(value="Bar")), datatypes_pb2.HumanName(text=datatypes_pb2.String(value="Bats")), ] self.assertEqual(proto_utils.field_content_length(patient, "name"), 0) for name in patient_names: proto_utils.append_value_at_field(patient, "name", name) self.assertEqual(proto_utils.field_content_length(patient, "name"), 3) self.assertEqual(patient.name[:], patient_names)
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)
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
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)