Nayden Van
Nayden Van

Reputation: 1569

Azure function Service bus has no attribute application_properties

I have an azure function ServiceBus on which I would like to customise its properties.

I have 2 topics, I send a message on topic 1(on which I do some processing) and send its output to the second topic. Due to the fact that this messages comes from different resources, I would like to have application properties that makes clear from where this message is coming.

so I have my code as follow:

def main(message: func.ServiceBusMessage):
    logging.info(message)
    print(message)
    message_content_type = message.content_type
    message_body = message.get_body().decode("utf-8")
    .....
    
    .....
    message1 = str(message_body)
    def send_output(sender):
        message_out = ServiceBusMessage(
        output_json,
        content_type="ModuleCommentAnalyzed", #setting the content type so that the service bus can route it.
        application_properties={b'source':message.application_properties[b'source']} #setting the tenant code
        )
        sender.send_messages(message_out)

But when i send a message, the function fails throwing the following error.

Result: Failure Exception: AttributeError: 'ServiceBusMessage' object has no attribute 'application_properties' Stack: File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/dispatcher.py", line 402, in _handle__invocation_request call_result = await self._loop.run_in_executor( File "/usr/local/lib/python3.9/concurrent/futures/thread.py", line 52, in run result = self.fn(*self.args, **self.kwargs) File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/dispatcher.py", line 606, in _run_sync_func return ExtensionManager.get_sync_invocation_wrapper(context, File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/extension.py", line 215, in _raw_invocation_wrapper result = function(**args) File "/home/site/wwwroot/function/testServiceBus.py", line 118, in main send_output(sender) File "/home/site/wwwroot/function/testServiceBus.py", line 108, in send_output application_properties={b'source':message.application_properties[b'source']} #setting the tenant code

I tried to replace message with variable message1 but in this case I get an error str does not contain an attribute...

Any help will be much appreciated to understand what I am doing wrong and better understand how this works.

Thank you so much in advance and please if you need more info just let me know

UPDATE.

According to Microsoft documentation, from servicebus 5.x they replace user_propertieswithapplicationProperties` I did tried both of them but nothing work.

Here is to make the OP a bit clear.

This is my updated code

import logging
import json

import azure.functions as func
from azure.servicebus import ServiceBusClient, ServiceBusMessage


def main(message: func.ServiceBusMessage):
    # Log the Service Bus Message as plaintext

    message_content_type = message.content_type
    message_body = message.get_body().decode("utf-8")
    result = json.dumps({
        'message_id': message.message_id,
        'body': message.get_body().decode('utf-8'),
        'content_type': message.content_type,
        'user_properties': message.user_properties,
        'metadata' : message.metadata
    })
    logging.info(result)

    # logging.info("Python ServiceBus topic trigger processed message.")
    # logging.info("Message Content Type: " + message_content_type)
    # logging.info("Message Body: " + message_body)

    CONN_STR = "XXX"

    topic_b = "topic_b"
    servicebus_client = ServiceBusClient.from_connection_string(conn_str=CONN_STR)
    def send_output(sender):
        message_out = ServiceBusMessage(
        result,
        content_type="application/json", #setting the content type so that the service bus can route it.
        user_properties={b'tenant': result.user_properties[b'MessageId']} #setting the tenant code
        )
        sender.send_messages(message_out)

    servicebus_client_out = servicebus_client.from_connection_string(conn_str=CONN_STR, logging_enable=True)
    
    with servicebus_client:
        sender = servicebus_client_out.get_topic_sender(topic_name=topic_b)
        with sender:
            send_output(sender)

For testing purpose, I am trying to add a custom property on with to return the message_id as it is part of the message coming in. But I get this error

Result: Failure Exception: AttributeError: 'str' object has no attribute 'user_properties' Stack

So I tried with sender

and the message was

Result: Failure Exception: AttributeError: 'ServiceBus' object has no attribute 'user_properties' Stack

This is something that I really cannot find anywhere in Microsoft documentation.

So to make sure that everything was just fine I tried with the following code

application_properties={'tenant': 'DEMO'}

And this worked just fine.

Please if anyone have any hint on this issue I would be grateful

Upvotes: 1

Views: 1902

Answers (2)

Nayden Van
Nayden Van

Reputation: 1569

@gvee Yes the documentation is totally contradictory and the library is not fully clear. I managed to solve the issue I and I hope this will help anyone in the future.

This issue has been solved for the library service-bus 7+

In my specific case I needed the function 2 perform 2 important steps. And they are as follow

  • from the incoming message on topic A.. get all the custom properties and save a specific property in a variable.
  • while sending the message from topic A to topic B..attach the saved property to the message before to send it out.

Now the main issue for me, as mentioned in my OP..was that the library was not able to recognise the attribute I was declaring. This was mainly due to the library having 2 attributes, user_properties AND application_properties.

So what I had to do in my code is as follow.

On the servicebus message I had to use user_properties to retrieve a specific property.

def main(message: func.ServiceBusMessage):
    logging.info(message)    
    test_user = message.user_properties['property']

Doing this I was able to extract the string I was looking for. so far so good.

Then I wanted to attach that test_user as a property to the message while sending it out to the topic B.. so I tried to use once again user_properties but this failed as the attribute was not recognised.

And here the weird think. I used application_properties at it worked, as follow

    def send_output(sender):
        message_out = ServiceBusMessage(
        output_json,
        content_type="ModuleCommentAnalyzed", #setting the content type so that the service bus can route it.
        application_properties={'property': test_user}
        )
        sender.send_messages(message_out)

So my conclusion was that to retrieve the property, I should use user_properties and to set a custom property I have to use application_properties.

I can't give a detailed explanation why it does work in this way, as documentation is not clear at all (at least for me) and the source code is not aligned correctly with the library functionality.

But I hope this will help somebody in the future.

Upvotes: 1

gvee
gvee

Reputation: 17161

I have to agree that the documentation is contradictory.

So, let's ignore the docs 😅 and go straight to the source: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py#L63

This tells us that the correct keyword argument is application_properties.

Reminder: this answer is pointing out the current (at the time of writing) code in the main branch. This appears to be for version 7+ of the azure-servicebus package.

Upvotes: 1

Related Questions