Skip to content

service: Support null (#79) - #189

Merged
phanak-sap merged 1 commit into
SAP:masterfrom
rettichschnidi:rs/upstream/support-null
Jan 31, 2022
Merged

phanak-sap merged 1 commit into
SAP:masterfrom
rettichschnidi:rs/upstream/support-null

Conversation

@rettichschnidi

@rettichschnidi rettichschnidi commented Jan 2, 2022 •

Copy link
Copy Markdown
Contributor

Until now, null values received from a OData server get substituted by a
default value.
Users of this library can not tell apart a value which coincidentally
equals the default, and a missing (null-ed) value received from the
server.

This commit allows the user to disable the substitution of null values by
default values, retrieving a None object instead.

Questions:

  • Is this an acceptable approach to implement this features?

In case the maintainers agree with the direction, I will add tests and documentation. (done)

@phanak-sap

phanak-sap commented Jan 2, 2022 •

Copy link
Copy Markdown
Contributor

I think it is worth doing if there is genuine need for this - but not like additional hardcoded parameter for client and service classes, but instead new, optional parameter for the Config, that is already consumed by them. Default should be false, correct, to retain current behavior.

def __init__(self,
custom_error_policies=None,
default_error_policy=None,
xml_namespaces=None):

It is the generic issue of specific problem of "Date valid minimum value vs default value" discussed in #143

But additionally, there is another layer of if-else possibilities - it depends also if the service metadata defines the attribute to be Nullable or not - see #102. if the retain_null=True and the service somehow returns Null on property that has metadata Nullable = False - it should fail obviously as currently is, not pass None object back.

@phanak-sap phanak-sap added the enhancement New feature or request label Jan 2, 2022
@rettichschnidi
rettichschnidi force-pushed the rs/upstream/support-null branch from 25073fa to 0d362a8 Compare January 12, 2022 00:03
@rettichschnidi

rettichschnidi commented Jan 12, 2022 •

Copy link
Copy Markdown
Contributor Author

I think it is worth doing if there is genuine need for this

For my use case (copying a 3rd-party database accessible via OData v2 into a SQL database) it is. Also, I think users generally expect to know when a value is missing (null), instead of getting a default value substituted.

I think it is worth doing if there is genuine need for this - but not like additional hardcoded parameter for client and service classes, but instead new, optional parameter for the Config, that is already consumed by them. Default should be false, correct, to retain current behavior.

I changed the Client to take the (extend) Config object. However, the server does not consume a Config, but a lit of arguments instead. Changing this would break the API.

But additionally, there is another layer of if-else possibilities - it depends also if the service metadata defines the attribute to be Nullable or not - see #102. if the retain_null=True and the service somehow returns Null on property that has metadata Nullable = False - it should fail obviously as currently is, not pass None object back.

Fixed it in the latest push.

Keeping as draft as I am still fighting the linter and I am unsure about how to document this properly.

@rettichschnidi
rettichschnidi force-pushed the rs/upstream/support-null branch from 0d362a8 to 9e2eb92 Compare January 23, 2022 23:43
@codecov-commenter

codecov-commenter commented Jan 23, 2022 •

Copy link
Copy Markdown

Codecov Report

Merging #189 (b83823e) into master (0599db2) will increase coverage by 0.11%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #189      +/-   ##
==========================================
+ Coverage   92.66%   92.77%   +0.11%     
==========================================
  Files           6        6              
  Lines        2768     2783      +15     
==========================================
+ Hits         2565     2582      +17     
+ Misses        203      201       -2     
Impacted Files Coverage Δ
pyodata/client.py 100.00% <100.00%> (ø)
pyodata/v2/model.py 93.38% <100.00%> (+0.07%) ⬆️
pyodata/v2/service.py 90.88% <100.00%> (+0.22%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0599db2...b83823e. Read the comment docs.

Comment thread docs/usage/advanced.rst Outdated
.. code-block:: python

count = getattr(northwind.entity_sets, 'Employees').get_entities().count().execute()
print(count)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation looks good except I am not sure how does the code sample relates to it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at all 😵‍💫 - this slipped in by accident and the documentation part is still WIP. Would like to give a "real-world" example using Northwind, just need to find some entity which actually returns a null property.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@filak-sap

filak-sap commented Jan 24, 2022 •

Copy link
Copy Markdown
Contributor

@rettichschnidi How does this change solve #79? Does the reporter need to configure the service instance with retain_null=True?

@rettichschnidi

rettichschnidi commented Jan 24, 2022 •

Copy link
Copy Markdown
Contributor Author

How does this change solve #79?

Empty dates will return Null/None, not the default value.

Does the reporter need to configure the service instance with retain_null=True?

This and checking if the (nullable) properties are None.

@rettichschnidi
rettichschnidi force-pushed the rs/upstream/support-null branch from 9e2eb92 to 551ea1f Compare January 26, 2022 00:55
@rettichschnidi
rettichschnidi marked this pull request as ready for review January 26, 2022 00:58
@rettichschnidi
rettichschnidi force-pushed the rs/upstream/support-null branch from 551ea1f to b83823e Compare January 26, 2022 01:02
Comment thread pyodata/v2/service.py Outdated
"""OData service"""

def __init__(self, url, schema, connection):
def __init__(self, url, schema, connection, retain_null=False):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not particularly like this API. generic parameters, followed by boolean flag.. it is not much future-proof, when need arise for fifth or sixth configuration parameter in service.

I understand that this is getting quite entangled an messy, because Config class is in the model.py module and so far was not used by service.py at at. And it should in fact be in the root next to client or exceptions, definitely above pyodata/v2, simply because same config parameters should work IMHO in future v4 support as well. Perhaps such move will be in the 2.0.0 version, where backward compatibility can be broken..

But service.py is already at the moment depending on model.py:

from . import model

So why not add the config=None to Service, instead of retain_null=False.. and in client.py:72 just pass the same value as in client.py:68

schema = pyodata.v2.model.MetadataBuilder(metadata, config=config).build()

@phanak-sap phanak-sap added this to the 1.9.0 milestone Jan 27, 2022
@filak-sap

Copy link
Copy Markdown
Contributor

Could you please rebase to the latest master (I don't like merge commits).

@rettichschnidi
rettichschnidi force-pushed the rs/upstream/support-null branch 3 times, most recently from d4dc0a9 to dfd4b4b Compare January 29, 2022 12:28
@rettichschnidi

rettichschnidi commented Jan 29, 2022 •

Copy link
Copy Markdown
Contributor Author

Rebased, using model.Config, improved commit messages and adjusted the documented example to #197.

@phanak-sap phanak-sap modified the milestones: 1.10.0, 1.9.0 Jan 31, 2022
Comment thread pyodata/client.py
# create service instance based on model we have
logger.info('Creating OData Service (version: %d)', odata_version)
service = pyodata.v2.service.Service(url, schema, connection)
service = pyodata.v2.service.Service(url, schema, connection, config=config)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, much better :)

@phanak-sap phanak-sap left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Please resolve the conflict in changelog.md, so we have nice one commit as so far and we are ready to merge to master (and release 1.9.0)

Until now, null values received from OData servers get substituted by
type specific default values.

Users could not differentiate between those substitutes and actual
values retrieved by the server, which might coincidentally equal the
default ones inserted by pyodata.

This commit allows the user to disable the substitution of null values,
retrieving a None object instead.

Example for Edm.Binary:

| Server | pyodata old behavior | pyodata new (retain_null=True) |
|--------+----------------------+--------------------------------|
|      0 |                    0 |                              0 |
|   null |                    0 |                           None |
@rettichschnidi
rettichschnidi force-pushed the rs/upstream/support-null branch from dfd4b4b to ff4d60b Compare January 31, 2022 12:18
@phanak-sap
phanak-sap merged commit 7627426 into SAP:master Jan 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants