-
Couldn't load subscription status.
- Fork 72
[SYNPY-1583] JSON Schema OOP models #1253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good start on these changes! Please re-request review when you are further along and want me to take another peek.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @andrewelamb ! Thanks for your hard work. For this round of review, I mainly focused on checking if the examples work and also the organization of the code. I have not yet checked the format of the docstring. Here's a summary:
- I am thinking if
list_json_schemasshould become a method underSchemaOrganizationlikeupdate_organization_acl? Currently, to list all the JSON schemas, we still have to do:
async def json_schema_list():
org = SchemaOrganization("linglp.test.schemas")
json_schemas = list_json_schemas("linglp.test.schemas", synapse_client=syn)
async for schema in json_schemas:
print(f"Schema: {schema}")
But I think we should be able to do: org.list_schemas() to list out all the json schemas in a given org.
- Would it make sense to follow our existing pattern of using
fill_from_dictto fill API responses and removefrom_responsecompletely? Something like:
class JsonSchemaOrganization:
def fill_from_dict(self, response: Dict[str, Any]) -> "JsonSchemaOrganization":
"""Converts a response from the REST API into this dataclass."""
self.name = response.get("name")
self.id = response.get("id")
self.created_on = response.get("createdOn")
self.created_by = response.get("createdBy")
return self
org = JsonSchemaOrganization(name="my.org")
org.fill_from_dict(api_response)
And then you can test fill_from_dict in a unit test to ensure all the attributes get filled correctly.
- I think for consistency
get_asyncshould always return either JSON Schemas or the organization, and not None.
As an example, the following currently returns None:
async def get_organization_info():
org = SchemaOrganization("linglp.test.schemas") # this organization actually exists
org.id = "12345" # Set ID manually to a random string
result = await org.get_async()
print(result)
asyncio.run(get_organization_info())
But I think I would want to reliably process the organization when I use this function in real world:
# This works sometimes, fails other times
org = SchemaOrganization("my.org.name")
result = await org.get_async()
# I don't want to do this:
if result:
print(f"Got org: {result.name}")
else:
print("Failed to get org")
I want to make sure this always works:
org = SchemaOrganization("my.org.name")
result = await org.get_async() # Always returns the org object
- I think all async examples need to be wrapped in async functions.
tests/unit/synapseclient/models/synchronous/unit_test_schema_organization.py
Outdated
Show resolved
Hide resolved
| } | ||
| ], | ||
| ) | ||
| def test_from_response_with_exception(self, response: dict[str, Any]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Tests emphasize TypeError, but should we also cover functional validation, such as missing required fields, empty lists/objects, and bad values to ensure robust handling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@linglp I apologize, I missed this comment until now. Could you elaborate?
|
@linglp Thanks for the early feedback: I'll start addressing your comments below: |
This gets all the JSONSchemas that are part of the org. Did you mean something different? The return will be changed to an AsyncGenerator shortly. |
The reason I have it like it for situations like
the |
tests/integration/synapseclient/models/async/test_schema_organization.py
Outdated
Show resolved
Hide resolved
tests/integration/synapseclient/models/synchronous/test_schema_organization.py
Outdated
Show resolved
Hide resolved
tests/integration/synapseclient/models/synchronous/test_schema_organization.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a few comments, but nothing major that I saw. Thanks for this work!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a quick first pass of schema_organization.py, I can review the other files later but wanted to give you these comments sooner rather than later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andrewelamb I tested out most parts, and most examples look good to me. Thanks for addressing my previous comments. But I think there's a bug when you get JSON Schema using JSONSchema.from_uri because it only gets you the latest version of the schema.
For testing, I created two versions of the schema:
test_org_name = "test.version.bug"
schema_name = "testVersionSchema"
schemas = JSONSchema(name=schema_name, organization_name=test_org_name)
print(schemas.get_body(version="4.0.0"))
print(schemas.get_body(version="1.0.0"))
And you should see two different versions being printed out which is correct.
But if I use from_uri, I got only version 4.0.0:
uri_v1 = f"{test_org_name}-{schema_name}-1.0.0"
schema_from_uri_v1 = JSONSchema.from_uri(uri_v1)
uri_v4 = f"{test_org_name}-{schema_name}-4.0.0"
schema_from_uri_v4 = JSONSchema.from_uri(uri_v4)
print(schema_from_uri_v1.get_body())
print(schema_from_uri_v4.get_body())
Also, when deleting a schema, assuming that you can do something like:
schema = JSONSchema(organization_name="my.org", name="test.schema")
schema.delete()
But what if I want to delete a specific version of the schema? How can I do that? Could you add an example?
| syn = Synapse() | ||
| syn.login() | ||
|
|
||
| org = SchemaOrganization("my.org.name") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the example here demonstrates how to store an organization. But we should demonstrate how to store a schema?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do, but that's done in the JSONSchema class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example is under JSONSchemaProtocol, so the example should be related to JSONSchema right? I saw that get method has an example related to the JSONSchema
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see what you mean. Thanks for catching that.
|
Thanks for checking this!
This is not really a bug. What's happening is that the JSONSchema object doesn't have a specific version. If you look at the attributes, there's no semantic version. It represents the superset of ALL versions of that schema. So when you use the
Note that The problem you are running into is with the
Yes, thanks for catching this! The delete methods now have a version argument that allows for deleting specific versions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @andrewelamb. LGTM! Thanks for addressing my previous comments.
If JSONSchema.from_uri("my.org-my.schema-0.0.4") returns a JSONSchema object, you still need to use veresion parameter to retrieve a specific version’s content, e.g.:
js = JSONSchema.from_uri("my.org-my.schema-0.0.4")
body = js.get_body(version="0.0.4")
This makes me wonder why we need from_uri at all. Wouldn’t it be simpler to do:
js = JSONSchema.from_uri(org="my.org", name="my.schema")
body = js.get_body(version="0.0.4")
|
@linglp You're correct that as opposed to: When I was working on something for Curator using the current functionality, this would have made my life easier. |
SYNPY-1583
Problem:
The Schema Organization and JSON Schema functionality was not done in a similar way to the other Synapse entities(OOP model).
Solution:
This refactors the use of the JSON Schema API endpoints in a more consistent way.