Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jrwdunham committed May 24, 2018
1 parent 452cf3b commit 951eca6
Show file tree
Hide file tree
Showing 4 changed files with 3,540 additions and 63 deletions.
291 changes: 254 additions & 37 deletions storage_service/locations/api/v3/remple/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,13 @@ def _set_crud_paths(self, paths, resource_name, resource_cls, pk_patt,

def _set_search_paths(self, paths, resource_name, resource_cls, pk_patt,
rsrc_collection_name):
query_builder = resource_cls._get_query_builder()
pprint.pprint(query_builder.schemata)
for action in ('search', 'search_post', 'new_search'):
http_method = {'search_post': 'post', 'new_search': 'get'}.get(
action, 'search')
operation_id = '{}_{}'.format(action, rsrc_collection_name)
modifiers = {'search_post': ['search'],
'new_search': ['new_search']}.get(
action, [])
action, [])
path = get_collection_targeting_openapi_path(
rsrc_collection_name, modifiers=modifiers)
path_dict = paths.setdefault(path, {})
Expand Down Expand Up @@ -252,59 +250,91 @@ def _get_query_schema_name(self, resource_name):
return 'SearchQueryOver{}'.format(
self.inflp.plural(resource_name).capitalize())

def _get_filter_schema_name(self, resource_name):
return 'FilterOver{}'.format(
self.inflp.plural(resource_name).capitalize())

def _get_new_search_schema_name(self, resource_name):
return 'DataForNewSearchOver{}'.format(
self.inflp.plural(resource_name).capitalize())

def _get_related_filter_schema_name(self, resource_name, attribute):
return 'FilterOver{}{}'.format(
self.inflp.plural(resource_name).capitalize(),
attribute.lower().capitalize())

def _get_coordinative_filter_schema_name(self, resource_name):
return 'CoordinativeFilterOver{}'.format(
self.inflp.plural(resource_name).capitalize())

def _get_negative_filter_schema_name(self, resource_name):
return 'NegativeFilterOver{}'.format(
self.inflp.plural(resource_name).capitalize())

def _get_simple_filter_schema_name(self, resource_name):
return 'SimpleFilterOver{}'.format(
self.inflp.plural(resource_name).capitalize())

# =========================================================================
# Schema path getters
# =========================================================================

def _get_read_schema_path(self, resource_name):
return '{}{}'.format(
SCHEMAS_ABS_PATH,
self._get_read_schema_name(resource_name))
return _schema_name2path(self._get_read_schema_name(resource_name))

def _get_mutate_schema_path(self, resource_name):
return '{}{}'.format(
SCHEMAS_ABS_PATH,
self._get_mutate_schema_name(resource_name))
return _schema_name2path(self._get_mutate_schema_name(resource_name))

def _get_edit_schema_path(self, resource_name):
return '{}{}'.format(
SCHEMAS_ABS_PATH,
self._get_edit_schema_name(resource_name))
return _schema_name2path(self._get_edit_schema_name(resource_name))

def _get_paginated_schema_path(self, resource_name):
return '{}{}'.format(
SCHEMAS_ABS_PATH,
self._get_paginated_schema_name(resource_name))
return _schema_name2path(self._get_paginated_schema_name(resource_name))

def _get_new_schema_path(self, resource_name):
return '{}{}'.format(
SCHEMAS_ABS_PATH,
self._get_new_schema_name(resource_name))
return _schema_name2path(self._get_new_schema_name(resource_name))

def _get_search_schema_path(self, resource_name):
return '{}{}'.format(
SCHEMAS_ABS_PATH,
self._get_search_schema_name(resource_name))
return _schema_name2path(self._get_search_schema_name(resource_name))

def _get_query_schema_path(self, resource_name):
return '{}{}'.format(
SCHEMAS_ABS_PATH,
self._get_query_schema_name(resource_name))
return _schema_name2path(self._get_query_schema_name(resource_name))

def _get_filter_schema_path(self, resource_name):
return _schema_name2path(self._get_filter_schema_name(resource_name))

def _get_coordinative_filter_schema_path(self, resource_name):
return _schema_name2path(self._get_coordinative_filter_schema_name(
resource_name))

def _get_negative_filter_schema_path(self, resource_name):
return _schema_name2path(self._get_negative_filter_schema_name(
resource_name))

def _get_simple_filter_schema_path(self, resource_name):
return _schema_name2path(self._get_simple_filter_schema_name(
resource_name))

def _get_related_filter_schema_path(self, resource_name, attribute):
return _schema_name2path(self._get_related_filter_schema_name(
resource_name, attribute))

def _get_related_filter_schema_paths_refs(self, resource_name,
resource_cfg):
return [
{'$ref':
self._get_related_filter_schema_path(resource_name, attribute)}
for attribute, _
in self._get_relational_attributes(resource_name, resource_cfg)]

def _get_new_search_schema_path(self, resource_name):
return '{}{}'.format(
SCHEMAS_ABS_PATH,
self._get_new_search_schema_name(resource_name))
return _schema_name2path(self._get_new_search_schema_name(resource_name))

def _get_error_schema_path(self):
return '{}{}'.format(SCHEMAS_ABS_PATH, ERROR_SCHEMA_NAME)
return _schema_name2path(ERROR_SCHEMA_NAME)

def _get_paginator_schema_path(self):
return '{}{}'.format(SCHEMAS_ABS_PATH, PAGINATOR_SCHEMA_NAME)
return _schema_name2path(PAGINATOR_SCHEMA_NAME)

# =========================================================================
# Response getters
Expand All @@ -323,7 +353,7 @@ def _get_create_responses(self, resource_name):
description='Bad request to create a new {}.'.format(
resource_name),
ref=self._get_error_schema_path())),
])
])

def _get_delete_responses(self, resource_name):
return OrderedDict([
Expand Down Expand Up @@ -653,10 +683,16 @@ def _get_schemas(self):
read_schema))
schemas[edit_schema_name] = edit_schema
if resource_cfg.get('searchable', True):
filter_schemas = (
self._get_filter_schemas(resource_name, resource_cfg,
read_schema))
for filter_schema_name, filter_schema in filter_schemas:
schemas[filter_schema_name] = filter_schema
query_schema_name, query_schema = (
self._get_query_schema(resource_name, resource_cfg,
read_schema))
schemas[query_schema_name] = query_schema

search_schema_name, search_schema = (
self._get_search_schema(resource_name, resource_cfg,
read_schema))
Expand Down Expand Up @@ -712,19 +748,196 @@ def _get_edit_schema(self, resource_name, resource_cfg, read_schema):
])
return edit_schema_name, edit_schema

def _get_query_schema(self, resource_name, resource_cfg, read_schema):
"""TODO/Question: can we describe the query schema better using arrays and oneOf?
def _get_filter_schemas(self, resource_name, resource_cfg, read_schema):
"""Each resource will generate multiple filter schemas: a coordinative
one, a negative one, a simple one and zero or more related (relational)
ones, depending on how many other resources (models) it is related to.
This method returns them as a list.
Note, there is a shorthand filter schema, which is based on arrays and
which is exemplified via the following (and which canNOT be described
using the OpenAPI spec)::
["and", [["Location", "purpose", "=", "AS"],
["Location", "description", "regex", "2018"]]]
["not", ["Location", "purpose", "=", "AS"]]
["Location", "purpose", "=", "AS"]
["Location", "space", "path", "like", "/usr/data/%"]
Then there is the long-hand filter schema, which is based on objects
and which is exemplified via the following (which CAN be described
using the OpenAPI spec)::
{"conjunction": "and",
"complement": [
{"attribute": "purpose",
"relation": "=",
"value": "AS"},
{"attribute": "description",
"relation": "regex",
"value": "2018"}]}
{"negator": "not",
"complement": {"attribute": "purpose",
"relation": "=",
"value": "AS"}}
{"attribute": "purpose", "relation": "=", "value": "AS"}
{"attribute": "space",
"subattribute": "path",
"relation": "like",
"value": "/usr/data/%"}
Note that the filter schema is inherently recursive and the Swagger-ui
web app cannot currently fully display a recursive schema. See
https://github.com/swagger-api/swagger-ui/issues/1679.
"""
query_schema_name = self._get_query_schema_name(resource_name)
query_schema = OrderedDict([
return (
[self._get_simple_filter_schema(resource_name, resource_cfg)] +
self._get_related_filter_schemas(resource_name, resource_cfg) +
[self._get_coordinative_filter_schema(resource_name, resource_cfg),
self._get_negative_filter_schema(resource_name, resource_cfg),
self._get_filter_schema(resource_name, resource_cfg)])

def _get_simple_filter_schema(self, resource_name, resource_cfg):
model_name = resource_cfg['resource_cls'].model_cls.__name__
simple_schema_name = self._get_simple_filter_schema_name(resource_name)
simple_schema = OrderedDict([
('type', 'object'),
('properties', OrderedDict([
('filter', OrderedDict([
('type', 'array'),
('items', OrderedDict([
('attribute', OrderedDict([
('type', 'string'),
('enum', self._get_simple_attributes(
model_name, resource_cfg)),
])),
('relation', OrderedDict([
('type', 'string'),
('enum', self._get_relations(resource_name, resource_cfg)),
])),
('value', {'anyOf': [{'type': 'string'},
{'type': 'number'},
{'type': 'boolean'},]}),
])),
])
return simple_schema_name, simple_schema

def _get_query_schemata(self, resource_cfg):
resource_cls = resource_cfg['resource_cls']
query_builder = resource_cls._get_query_builder()
return query_builder.schemata

def _get_relational_attributes(self, resource_name, resource_cfg):
resource_cls = resource_cfg['resource_cls']
model_name = resource_cfg['resource_cls'].model_cls.__name__
resource_cls_name = resource_cls.__name__
query_schemata = self._get_query_schemata(resource_cfg)
return [(attr, cfg.get('foreign_model'))
for attr, cfg in
query_schemata[model_name].items()
if cfg.get('foreign_model')]

def _get_simple_attributes(self, model_cls_name, resource_cfg):
resource_cls = resource_cfg['resource_cls']
resource_cls_name = resource_cls.__name__
query_schemata = self._get_query_schemata(resource_cfg)
return [attr for attr, cfg in
query_schemata[model_cls_name].items()
if not cfg.get('foreign_model')]

def _get_related_attributes(self, resource_cfg, related_model_name):
query_schemata = self._get_query_schemata(resource_cfg)
return [attr for attr, cfg in query_schemata[related_model_name].items()
if not cfg.get('foreign_model')]

def _get_relations(self, resource_name, resource_cfg):
resource_cls = resource_cfg['resource_cls']
query_builder = resource_cls._get_query_builder()
return list(query_builder.relations)

def _get_related_filter_schemas(self, resource_name, resource_cfg):
schemas = []
for attribute, related_model_name in self._get_relational_attributes(
resource_name, resource_cfg):
related_schema_name = self._get_related_filter_schema_name(
resource_name, attribute)
related_schema = OrderedDict([
('type', 'object'),
('properties', OrderedDict([
('attribute', OrderedDict([
('type', 'string'),
('enum', [attribute]),
])),
('subattribute', OrderedDict([
('type', 'string'),
('enum', self._get_related_attributes(
resource_cfg, related_model_name)),
])),
('relation', OrderedDict([
('type', 'string'),
('enum', self._get_relations(resource_name, resource_cfg)),
])),
('value', {'anyOf': [{'type': 'string'},
{'type': 'number'},
{'type': 'boolean'},]}),
])),
])
schemas.append((related_schema_name, related_schema))
return schemas

def _get_coordinative_filter_schema(self, resource_name, resource_cfg):
coord_schema_name = self._get_coordinative_filter_schema_name(resource_name)
coord_schema = OrderedDict([
('type', 'object'),
('properties', OrderedDict([
('conjunction', OrderedDict([
('type', 'string'),
('enum', ['and', 'or']),
])),
('complement', OrderedDict([
('type', 'array'),
('items', {'$ref':
self._get_filter_schema_path(resource_name)}),
])),
])),
])
return coord_schema_name, coord_schema

def _get_negative_filter_schema(self, resource_name, resource_cfg):
neg_schema_name = self._get_negative_filter_schema_name(resource_name)
neg_schema = OrderedDict([
('type', 'object'),
('properties', OrderedDict([
('negator', OrderedDict([
('type', 'string'),
('enum', ['not']),
])),
('complement',
{'$ref': self._get_filter_schema_path(resource_name)}),
])),
])
return neg_schema_name, neg_schema

def _get_filter_schema(self, resource_name, resource_cfg):
filter_schema_name = self._get_filter_schema_name(resource_name)
filter_schema = {
'oneOf': [
{'$ref': self._get_coordinative_filter_schema_path(
resource_name)},
{'$ref': self._get_negative_filter_schema_path(resource_name)},
{'$ref': self._get_simple_filter_schema_path(resource_name)},
] + self._get_related_filter_schema_paths_refs(resource_name,
resource_cfg)
}
return filter_schema_name, filter_schema

def _get_query_schema(self, resource_name, resource_cfg, read_schema):
query_schema_name = self._get_query_schema_name(resource_name)
query_schema = OrderedDict([
('type', 'object'),
('properties', OrderedDict([
('filter', {'$ref':
self._get_filter_schema_path(resource_name)}),
('order_by', OrderedDict([
('type', 'array'),
('items', OrderedDict([
Expand Down Expand Up @@ -1112,3 +1325,7 @@ def _summarize(action, resource_name, rsrc_collection_name):
rsrc_collection_name),
),
}[action]


def _schema_name2path(schema_name):
return '{}{}'.format(SCHEMAS_ABS_PATH, schema_name)
4 changes: 2 additions & 2 deletions storage_service/locations/api/v3/remple/querybuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,13 +616,13 @@ def schemata(self):
if field_cls_name == 'OneToOneRel':
field_name = field.get_accessor_name()
field_val = {'foreign_model': field.related_model.__name__,
'type': 'scalar'}
'type': 'scalar'}
elif field_cls_name in ('ManyToManyField', 'ManyToManyRel',
'ManyToOneRel',):
if field_cls_name == 'ManyToOneRel':
field_name = field.get_accessor_name()
field_val = {'foreign_model': field.related_model.__name__,
'type': 'collection'}
'type': 'collection'}
model_schema[field_name] = field_val
_schemata[model_cls_name] = model_schema
self._schemata = _schemata
Expand Down
Loading

0 comments on commit 951eca6

Please sign in to comment.