Skip to content
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

anyOf, allOf, etc. in /info/{entry_type} endpoints #775

Closed
CasperWA opened this issue Apr 8, 2021 · 3 comments
Closed

anyOf, allOf, etc. in /info/{entry_type} endpoints #775

CasperWA opened this issue Apr 8, 2021 · 3 comments
Labels
bug Something isn't working models For issues related to the pydantic models directly priority/medium Issue or PR with a consensus of medium priority

Comments

@CasperWA
Copy link
Member

CasperWA commented Apr 8, 2021

It seems via sheer luck this has not broken in the current implementation.
The use of retrieve_queryable_properties() breaks if any of the attributes/properties are subclasses of pydantic.BaseModel or is an enumeration.

Since this function is only used for /info/structures and /info/references and neither of StructureResourceAttributes and ReferenceResourceAttributes have attributes with types of BaseModel subclasses or enumerations directly (they're always wrapped in a list or similar), this never becomes an issue - but it will be an issue for custom resources for implementations that are based on this package, using the utility functions, etc.

For the BaseModel subclasses one can utilize the __modify_schema__ method (see the pydantic docs here and here).
However, for enumerations, one needs to set the type value directly when specifying the attribute that has the enumeration type, e.g.:

from enum import Enum
from pydantic import BaseModel, Field

class SomeEnum(Enum):
    test = "test"

class SomeModel(BaseModel):
    my_enum: SomeEnum = Field(..., description="My enumeration", type="enum")

Either one does it like this, or (perhaps more straightforward) we handle allOf and anyOf keys for an OpenAPI schema property, go to the referenced model(s) and retrieve the type from there, which should be the same, and present. The main issue here will be anyOf, which results from a Union type annotation, i.e., it could be a set of different types. This is however, not supported by OPTIMADE (essentially), and as such this should probably raise an exception if it's found that there is not a single unique type from all the possible models/types listed in the anyOf list value.

I hope this explanation makes a bit of sense - it's an issue I encountered in the OPTIMADE gateway development, and will lead me to have to develop a specialized retrieve_queryable_properties() function, essentially to deal with this.

@CasperWA CasperWA added bug Something isn't working priority/low Issue or PR with a consensus of low priority models For issues related to the pydantic models directly labels Apr 8, 2021
@CasperWA
Copy link
Member Author

CasperWA commented Apr 8, 2021

Note. The solution I am using now is to simply explicitly specify type in the Field() for each of the attributes with BaseModel and Enum types.

@ml-evs
Copy link
Member

ml-evs commented May 27, 2021

I've also just run into this when trying to use retrieve_queryable_properties inside the entry mapper code (which means it also gets used for the enum-heavy LinksResource model...

@ml-evs ml-evs added priority/medium Issue or PR with a consensus of medium priority and removed priority/low Issue or PR with a consensus of low priority labels May 27, 2021
@fekad
Copy link
Contributor

fekad commented Sep 25, 2021

you can find a code snippet below for getting rid of all "ref"s:

import copy

def schema_without_refs(schema: dict) -> dict:
    
    definitions = set()
    schema = copy.deepcopy(schema)

    def get_reference(path):
        dct = schema
        for key in path:
            dct = dct[key]
        return dct

    # recursively walking through the dictionary
    def inner_loop(item):
        if isinstance(item, list):
            for i, v in enumerate(item):
                item[i] = inner_loop(v)

        elif isinstance(item, dict):
            if '$ref' in item.keys():
                path = item['$ref'][2:].split('/')
                definitions.add(path[0])
                return get_reference(path)
                
            for k, v in item.items():
                item[k] = inner_loop(v)
            
        return item
    
    new_schema = inner_loop(schema)
    
    # removing original definitions
    for k in definitions:
        del new_schema[k]

    return new_schema

usage:

from odbx.models.structure import MatadorHamiltonian
import json

m = MatadorHamiltonian.schema()
d = schema_without_refs(m)
print(json.dumps(d, indent=2))

@ml-evs ml-evs closed this as not planned Won't fix, can't repro, duplicate, stale Mar 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working models For issues related to the pydantic models directly priority/medium Issue or PR with a consensus of medium priority
Projects
None yet
Development

No branches or pull requests

3 participants