Skip to content

Conversation

@NBIX-Mark-Southern
Copy link
Contributor

For discussion; my OData source fails inserts and updates when payloads:

  1. contain @odata.type
  2. @odata.bind's do not have a slash prefix
  3. properties with empty values are included.

I had a look at an existing PR but have reservations as every single call to save or update must pass state and increase client code complexity. Instead, I created some Class properties in the EntityState class so that it's payload generation is customizable. These can be set once only in a session though I guess this approach would fail if more than one incompatible odata source were being used at the same time. A third possibility might be to pass parameters through the ODataService, store in the Context and pass to the EntityState that way.

Thoughts welcome, thank you.

@NBIX-Mark-Southern NBIX-Mark-Southern changed the title Customizable data package for update and insert Customizable payloads for update and insert Feb 21, 2025
@NBIX-Mark-Southern
Copy link
Contributor Author

For readers, my current workaround is a custom Session...

class PrepareSession(requests.Session):
    def prepare_request(self, request: Request) -> PreparedRequest:
        prepared_request = super().prepare_request(request)
        if prepared_request.method in ["POST", "PUT"] and prepared_request.body:
            body = {}
            for key, value in json.loads(prepared_request.body).items():
                if value is None:
                    continue
                if key == "@odata.type":
                    continue
                if key.endswith("@odata.bind") and not value.startswith("/"):
                    value = f"/{value}"
                body[key] = value
            prepared_request.body = json.dumps(body)
            prepared_request.headers["Content-Length"] = str(len(prepared_request.body))
        return prepared_request

@eblis
Copy link
Owner

eblis commented Feb 22, 2025

I'm currently thinking that the 3rd option you suggested would be best.
I'm thinking that we could pass in an instance of a new class ODataServerFlags into ODataService, and the flags class could be something like:

@dataclass
class ODataServerFlags:
  skip_null_properties: bool = False
  provide_odata_type_annotation: bool = False
  odata_bind_requires_slash: bool = False

We should always pass an instance of the flags, we could have a default instance for the user if they don't want to specify it where the defaults are the current behaviour of the library.
Then in the future if other weird/different OData servers are encountered we can enhance the flags class to support those other use cases.

What do you think?

@NBIX-Mark-Southern
Copy link
Contributor Author

I think you are exactly right, a combination of a configuration/flags class and passing through the service is definitely the way to go.

I've updated the PR with an implementation of this. A bit more rigorous this time, I found and fixed some tests that were broken in my environment and TestCompositeKeys.test_update_entity which was broken by my PR #10!

@NBIX-Mark-Southern
Copy link
Contributor Author

NBIX-Mark-Southern commented Feb 27, 2025 via email

@eblis eblis merged commit 893855c into eblis:master Feb 28, 2025
@eblis
Copy link
Owner

eblis commented Feb 28, 2025

I'm sorry, was a bit busy with other tasks, I saw the PR but didn't have time until today.
I released version 0.6.3 with all the changes.

Hope it works out for you.

@NBIX-Mark-Southern
Copy link
Contributor Author

Not at all, thank you so much for such a helpful project. Maybe sometime in future we can talk about batched odata!

@NBIX-Mark-Southern NBIX-Mark-Southern deleted the customize_entity_state branch February 28, 2025 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants