async def test_post_invalid_json(self, init_db, headers, root_path, client): client = await client user = [{ 'name': 'test2', 'email': 'test2', 'password': '******', 'grants': [{ 'test': 1, 'method_id': 1 }] }] resp = await client.post('/users', data=ujson.dumps(user), headers=headers) assert resp.status == 400 result = (await resp.json()) message = result.pop('message') expected_schema = ujson.dumps(get_swagger_json(root_path + '/../myreco/users/models.py'), escape_forward_slashes=False) expected_schema = \ expected_schema.replace('#/definitions/grants', '#/definitions/UsersModel.grants')\ .replace('#/definitions/method', '#/definitions/UsersModel.method')\ .replace('#/definitions/uri', '#/definitions/UsersModel.uri') expected_schema = ujson.loads(expected_schema) fail_msg = "Failed validating instance['0']['grants']['0'] "\ "for schema['items']['allOf']['1']['properties']['grants']['items']['oneOf']" assert message == \ "{'method_id': 1, 'test': 1} is not valid under any of the given schemas. " + fail_msg \ or message == \ "{'test': 1, 'method_id': 1} is not valid under any of the given schemas. " + fail_msg assert result == { 'instance': {'method_id': 1, 'test': 1}, 'schema': expected_schema['definitions']['grants'] }
class StoresModelBase(AbstractConcreteBase): __tablename__ = 'stores' __swagger_json__ = get_swagger_json(__file__) __table_args__ = (sa.UniqueConstraint('name', 'country'), ) id = sa.Column(sa.Integer, primary_key=True) name = sa.Column(sa.String(255), nullable=False) country = sa.Column(sa.String(255), nullable=False) configuration_json = sa.Column(sa.Text, nullable=False) @property def configuration(self): if not hasattr(self, '_configuration'): self._configuration = ujson.loads(self.configuration_json) return self._configuration async def _setattr(self, attr_name, value, session, input_): if attr_name == 'configuration': value = ujson.dumps(value) attr_name = 'configuration_json' await super()._setattr(attr_name, value, session, input_) def _format_output_json(self, dict_inst, schema): if schema.get('configuration') is not False: config = dict_inst.pop('configuration_json') dict_inst['configuration'] = self.configuration
def extend_swagger_json(original, current_filename, swagger_json_name=None, by_method=False): swagger_json = deepcopy(original) additional_swagger = get_swagger_json(current_filename, swagger_json_name) if by_method: for path_name, addit_path in additional_swagger['paths'].items(): if path_name in swagger_json['paths']: path = swagger_json['paths'][path_name] for method_name, method in addit_path.items(): if method_name in path: path[method_name].update(method) else: path[method_name] = method else: swagger_json['paths'].update(additional_swagger['paths']) definitions = swagger_json.get('definitions') additional_definitions = additional_swagger.get('definitions') if additional_definitions: if definitions: definitions.update(additional_definitions) else: swagger_json['definitions'] = additional_definitions return swagger_json
def _validate_input(self, schema): validate(schema, get_swagger_json(__file__, 'store_items_metaschema.json')) for id_name in schema['id_names']: if id_name not in schema.get('properties', {}): raise ValidationError( "id_name '{}' was not found in schema properties".format( id_name), instance=schema['id_names'], schema=schema)
class EngineStrategiesModelBase(AbstractConcreteBase): __tablename__ = 'engine_strategies' __swagger_json__ = get_swagger_json(__file__) _jobs = dict() __table_args__ = (sa.UniqueConstraint('class_name', 'class_module'), ) id = sa.Column(sa.Integer, primary_key=True) name = sa.Column(sa.String(255), unique=True, nullable=False) class_name = sa.Column(sa.String(255), nullable=False) class_module = sa.Column(sa.String(255), nullable=False) async def _validate(self, session, input_): self.get_class() @classmethod def _get_class(cls, strategy): return ModuleObjectLoader.load({ 'path': strategy['class_module'], 'object_name': strategy['class_name'] }) def get_class(self): if not hasattr(self, '_class'): self._class = type(self)._get_class( self.todict({'object_types': False})) return self._class @property def object_types(self): return self.get_class().object_types.keys() def _format_output_json(self, dict_inst, schema): if schema.get('object_types') is not False: dict_inst['object_types'] = self.object_types @classmethod def get_instance(cls, engine, items_model=None): return cls._get_class(engine['strategy'])(engine, items_model)
class GrantsModelBase(AbstractConcreteBase): __tablename__ = 'grants' __table_args__ = (sa.UniqueConstraint('uri_id', 'method_id'), ) __swagger_json__ = get_swagger_json(__file__, 'grants_swagger.json') id = sa.Column(sa.Integer, primary_key=True) @declared_attr def uri_id(cls): return sa.Column(sa.ForeignKey('uris.id')) @declared_attr def method_id(cls): return sa.Column(sa.ForeignKey('methods.id')) @declared_attr def uri(cls): return sa.orm.relationship('URIsModel') @declared_attr def method(cls): return sa.orm.relationship('MethodsModel')
class _ItemTypesModelBase(AbstractConcreteBase): __tablename__ = 'item_types' __swagger_json__ = get_swagger_json(__file__) __schema_dir__ = get_dir_path(__file__) id = sa.Column(sa.Integer, primary_key=True) name = sa.Column(sa.String(255), unique=True, nullable=False) schema_json = sa.Column(sa.Text, nullable=False) store_items_class_json = sa.Column(sa.Text) @declared_attr def stores(cls): return sa.orm.relationship('StoresModel', uselist=True, secondary='item_types_stores') @property def store_items_class(self): if not hasattr(self, '_store_items_class'): self._store_items_class = \ ujson.loads(self.store_items_class_json) if self.store_items_class_json \ is not None else None return self._store_items_class async def _setattr(self, attr_name, value, session, input_): if attr_name == 'schema': self._validate_input(value) value = ujson.dumps(value) attr_name = 'schema_json' if attr_name == 'store_items_class': value = ujson.dumps(value) attr_name = 'store_items_class_json' await super()._setattr(attr_name, value, session, input_) def _validate_input(self, schema): validate(schema, get_swagger_json(__file__, 'store_items_metaschema.json')) for id_name in schema['id_names']: if id_name not in schema.get('properties', {}): raise ValidationError( "id_name '{}' was not found in schema properties".format( id_name), instance=schema['id_names'], schema=schema) def _format_output_json(self, dict_inst, todict_schema): if todict_schema.get('schema') is not False: if 'schema_json' in dict_inst: dict_inst['schema'] = ujson.loads(dict_inst.pop('schema_json')) schema_properties = dict_inst['schema'].get('properties', {}) schema_properties_names = sorted(schema_properties.keys()) dict_inst['available_filters'] = \ [{'name': name, 'schema': schema_properties[name]} \ for name in schema_properties_names if name != '_operation'] if todict_schema.get('store_items_class') is not False: dict_inst.pop('store_items_class_json') dict_inst['store_items_class'] = self.store_items_class @classmethod async def swagger_get_filter_types(cls, req, session): filters_factory = cls.get_model('slot_filters').__factory__ body = ujson.dumps(filters_factory.get_filter_types()) return cls._build_response(200, body=body)
import sqlalchemy as sa from jsonschema import ValidationError, validate from jsonschema.validators import Draft4Validator, create from myreco.engine_strategies.filters.filters import BooleanFilterBy from myreco.item_types._store_items_model_meta import _StoreItemsModelBaseMeta from myreco.utils import ModuleObjectLoader, build_class_name, build_item_key from sqlalchemy.ext.declarative import AbstractConcreteBase, declared_attr from swaggerit.method import SwaggerMethod from swaggerit.models.orm.factory import FactoryOrmModels from swaggerit.request import SwaggerRequest from swaggerit.utils import get_dir_path, get_swagger_json import ujson store_items_metaschema = get_swagger_json(__file__, 'store_items_metaschema.json') ItemValidator = create(store_items_metaschema, Draft4Validator.VALIDATORS) ItemValidator.DEFAULT_TYPES['simpleObject'] = dict class _ItemTypesModelBase(AbstractConcreteBase): __tablename__ = 'item_types' __swagger_json__ = get_swagger_json(__file__) __schema_dir__ = get_dir_path(__file__) id = sa.Column(sa.Integer, primary_key=True) name = sa.Column(sa.String(255), unique=True, nullable=False) schema_json = sa.Column(sa.Text, nullable=False) store_items_class_json = sa.Column(sa.Text) @declared_attr
class PlacementsModelBase(AbstractConcreteBase): __tablename__ = 'placements' __swagger_json__ = get_swagger_json(__file__) hash = sa.Column(sa.String(255), unique=True, nullable=False) small_hash = sa.Column(sa.String(255), primary_key=True) name = sa.Column(sa.String(255), nullable=False) ab_testing = sa.Column(sa.Boolean, default=False) show_details = sa.Column(sa.Boolean, default=True) distribute_items = sa.Column(sa.Boolean, default=False) is_redirect = sa.Column(sa.Boolean, default=False) @declared_attr def store_id(cls): return sa.Column(sa.ForeignKey('stores.id'), nullable=False) @declared_attr def variations(cls): return sa.orm.relationship('VariationsModel', uselist=True, passive_deletes=True) async def init(self, session, input_=None, **kwargs): await super().init(session, input_=input_, **kwargs) self._set_hash() def _set_hash(self): if self.name and not self.hash: hash_ = hashlib.new('ripemd160') hash_.update(self.name.encode() + bytes(self.store_id)) self.hash = hash_.hexdigest() def __setattr__(self, name, value): if name == 'hash': self.small_hash = value[:5] super().__setattr__(name, value) @classmethod async def get_items(cls, req, session): placement = await cls._get_placement(req, session) if placement is None: return cls._build_response(404) explict_fallbacks = req.query.pop('explict_fallbacks', False) input_external_variables = req.query show_details = req.query.pop('show_details', placement.get('show_details')) distribute_items = placement.get('distribute_items') recos = slots = [] recos_key = 'slots' slots_coros = [] for slot in placement['variations'][0]['slots']: coro = cls._get_slot_recos_async(slot, input_external_variables, session, show_details) slots_coros.append(Task(coro)) for coro in slots_coros: slots.append(await coro) valid_slots = [] for slot in slots: slot_recos = slot['items'] has_fallback = [ True for fallback in slot_recos['fallbacks'] if fallback ] if slot_recos['main'] or has_fallback: valid_slots.append(slot) if not valid_slots: return cls._build_response(404) if not explict_fallbacks or placement['is_redirect']: for slot in slots: slot['items'] = cls._get_all_slot_recos(slot['items']) if distribute_items: recos_key = 'distributed_items' recos = cls._get_all_recos_from_slots(slots) recos = cls._distribute_items(recos) if placement['is_redirect']: return cls._build_redirect_response(recos, distribute_items, req) else: placement = { 'name': placement['name'], 'small_hash': placement['small_hash'] } placement[recos_key] = recos return cls._build_recos_response(placement) @classmethod async def _get_placement(cls, req, session): small_hash = req.path_params['small_hash'] placements = await cls.get(session, {'small_hash': small_hash}) if not placements: return None return placements[0] @classmethod async def _get_slot_recos_async(cls, slot, input_external_variables, session, show_details): slot_recos = {'fallbacks': []} slot_recos['main'] = \ await cls._get_recos_by_slot(slot, input_external_variables, session, show_details) await cls._get_fallbacks_recos(slot_recos, slot, input_external_variables, session, show_details) slot = { 'name': slot['name'], 'item_type': slot['engine']['item_type']['name'] } slot['items'] = slot_recos return slot @classmethod async def _get_recos_by_slot(cls, slot, input_external_variables, session, show_details, max_items=None): try: engine = slot['engine'] items_model = get_items_model(engine['item_type'], engine['store_id']) engine_vars = cls._get_slot_variables(slot, input_external_variables) filters = cls._get_slot_filters(slot, input_external_variables, items_model) engine_strategy_model = cls.get_model('engine_strategies') strategy_instance = engine_strategy_model.get_instance( engine, items_model) max_items = slot['max_items'] if max_items is None else max_items return await strategy_instance.get_items(session, filters, max_items, show_details, items_model, **engine_vars) except Exception as error: cls._logger.debug('Input Variables:\n' + ujson.dumps(input_external_variables, indent=4)) cls._logger.exception('Slot:\n' + ujson.dumps(slot, indent=4)) return [] @classmethod def _get_slot_variables(cls, slot, input_external_variables): engine_vars = dict() for slot_var in slot['slot_variables']: var_name = slot_var['external_variable']['name'] var_engine_name = slot_var['engine_variable_name'] if var_name in input_external_variables: var_value = input_external_variables[var_name] schema = cls._get_external_variable_schema( slot, var_engine_name) if schema is not None: engine_vars[var_engine_name] = JsonBuilder.build( var_value, schema) return engine_vars @classmethod def _get_external_variable_schema(cls, slot, var_name): for var in slot['engine']['variables']: if var['name'] == var_name: return var['schema'] @classmethod def _get_slot_filters(cls, slot, input_external_variables, items_model): filters = dict() for slot_filter in slot['slot_filters']: var_name = slot_filter['external_variable']['name'] prop_name = slot_filter['property_name'] is_overridable = slot_filter['override'] and slot_filter[ 'override_value'] if var_name in input_external_variables: if is_overridable: var_value = slot_filter['override_value'] else: var_value = input_external_variables[var_name] cls._set_filter(filters, slot_filter, slot, var_value, items_model) elif is_overridable: var_value = slot_filter['override_value'] cls._set_filter(filters, slot_filter, slot, var_value, items_model) return filters @classmethod def _set_filter(cls, filters, slot_filter, slot, var_value, items_model): factory = cls.get_model('slot_filters').__factory__ filter_schema, input_schema = \ cls._get_filter_and_input_schema(slot['engine'], slot_filter) if filter_schema is not None and input_schema is not None: filter_ = factory.make(items_model, slot_filter, filter_schema) filters[filter_] = JsonBuilder.build(var_value, input_schema) @classmethod def _get_filter_and_input_schema(cls, engine, slot_filter): for var in engine['item_type']['available_filters']: if var['name'] == slot_filter['property_name']: if slot_filter['type_id'] == 'item_property_value' or \ slot_filter['type_id'] == 'item_property_value_index': input_schema = { 'type': 'array', 'items': { 'type': 'string' } } elif var['schema'].get('type') != 'array': input_schema = {'type': 'array', 'items': var['schema']} else: input_schema = var['schema'] filter_schema = var['schema'] return filter_schema, input_schema return None, None @classmethod async def _get_fallbacks_recos(cls, slot_recos, slot, input_external_variables, session, show_details): if len(slot_recos['main']) != slot['max_items']: for fallback in slot['fallbacks']: fallbacks_recos_size = \ sum([len(fallback) for fallback in slot_recos['fallbacks']]) max_items = slot['max_items'] - len( slot_recos['main']) - fallbacks_recos_size if max_items == 0: break fallback_recos = await cls._get_recos_by_slot( fallback, input_external_variables, session, show_details, max_items) all_recos = cls._get_all_slot_recos(slot_recos) fallback_recos = cls._unique_recos(fallback_recos, all_recos) slot_recos['fallbacks'].append(fallback_recos) @classmethod def _get_all_slot_recos(cls, slot_recos): all_recos = list(slot_recos['main']) [ all_recos.extend(fallback_recos) for fallback_recos in slot_recos['fallbacks'] ] return all_recos @classmethod def _get_all_recos_from_slots(cls, slots): recos = [] for slot in slots: for reco in slot['items']: reco['type'] = slot['item_type'] recos.append(slot['items']) return recos @classmethod def _distribute_items(cls, recos_list, random=True): total_length = sum([len(recos) for recos in recos_list]) total_items = [] initial_pos = 0 for recos in recos_list: if not recos: continue step = int(total_length / len(recos)) if random: initial_pos = random_.randint(0, step - 1) positions = range(initial_pos, total_length, step) zip_items_positions = zip(recos, positions) total_items.extend(zip_items_positions) random_.shuffle(total_items) sorted_items = sorted(total_items, key=(lambda each: each[1])) return [i[0] for i in sorted_items] @classmethod def _unique_recos(cls, recos, all_recos): unique = list() [unique.append(reco) for reco in recos if not all_recos.count(reco)] return unique @classmethod def _build_redirect_response(cls, recos, distribute_items, req): slot_idx = req.query.get('slot_idx') item_idx = req.query.get('item_idx') if item_idx is None: message = { 'message': "Query argument 'item_idx' "\ "is mandatory when 'is_redirect' is true." } return cls._build_response(400, body=cls._pack_obj(message)) if (slot_idx is None and distribute_items is False): message = { 'message': "Query argument 'slot_idx' "\ "is mandatory when 'distribute_items' is false." } return cls._build_response(400, body=cls._pack_obj(message)) elif (slot_idx is not None and distribute_items is True): message = { 'message': "Query argument 'slot_idx' "\ "can't be setted when 'distribute_items' is true." } return cls._build_response(400, body=cls._pack_obj(message)) if distribute_items is True: get_item = lambda recos: recos[item_idx] else: get_item = lambda recos: recos[slot_idx]['items'][item_idx] try: location = get_item(recos) except IndexError: return cls._build_response(404) else: return cls._build_response(302, headers={'Location': str(location)}) @classmethod def _build_recos_response(cls, recos): headers = {'Content-Type': 'application/json'} return cls._build_response(200, body=cls._pack_obj(recos), headers=headers)
class SlotsModelBase(AbstractConcreteBase): __tablename__ = 'slots' __swagger_json__ = get_swagger_json(__file__) id = sa.Column(sa.Integer, primary_key=True) max_items = sa.Column(sa.Integer, nullable=False) name = sa.Column(sa.String(255), nullable=False) @declared_attr def engine_id(cls): return sa.Column(sa.ForeignKey('engines.id'), nullable=False) @declared_attr def store_id(cls): return sa.Column(sa.ForeignKey('stores.id'), nullable=False) @declared_attr def engine(cls): return sa.orm.relationship('EnginesModel') @declared_attr def slot_variables(cls): return sa.orm.relationship('SlotVariablesModel', uselist=True, passive_deletes=True) @declared_attr def slot_filters(cls): return sa.orm.relationship('SlotFiltersModel', uselist=True, passive_deletes=True) @declared_attr def fallbacks(cls): return sa.orm.relationship( 'SlotsModel', uselist=True, remote_side='SlotsModel.id', secondary='slots_fallbacks', primaryjoin='slots_fallbacks.c.slot_id == SlotsModel.id', secondaryjoin='slots_fallbacks.c.fallback_id == SlotsModel.id') async def init(self, session, input_=None, **kwargs): await super().init(session, input_=input_, **kwargs) self._validate_fallbacks(input_) self._validate_slot_variables(input_) self._validate_slot_filters(input_) def _validate_fallbacks(self, input_): for fallback in self.fallbacks: if fallback.id == self.id: raise SwaggerItModelError( "a Engine Manager can't fallback itself", input_) if fallback.engine.item_type_id != self.engine.item_type_id: raise SwaggerItModelError( "Cannot set a fallback with different items types", input_) def _validate_slot_variables(self, input_): if self.engine is not None: engine = self.engine.todict() engine_variables_set = set( [var['name'] for var in engine['variables']]) message = "Invalid slot variable with 'engine_variable_name' attribute value '{}'" schema = {'available_variables': engine['variables']} for slot_variable in self.slot_variables: var_name = slot_variable.engine_variable_name if var_name not in engine_variables_set: raise ValidationError(message.format(var_name), instance=input_, schema=schema) def _validate_slot_filters(self, input_): if self.engine is not None: engine = self.engine.todict() available_filters = engine['item_type']['available_filters'] available_filters_set = set( [filter_['name'] for filter_ in available_filters]) schema = {'available_filters': available_filters} message = "Invalid slot filter with 'property_name' attribute value '{}'" for slot_filter in self.slot_filters: filter_name = slot_filter.property_name if filter_name not in available_filters_set: raise ValidationError(message.format(filter_name), instance=input_, schema=schema) async def _setattr(self, attr_name, value, session, input_): if attr_name == 'engine_id': value = {'id': value} attr_name = 'engine' if attr_name == 'slot_variables': for engine_var in value: if 'external_variable_id' in engine_var: var = {'id': engine_var.pop('external_variable_id')} engine_var['external_variable'] = var await super()._setattr(attr_name, value, session, input_) def _format_output_json(self, dict_inst, schema): if schema.get('fallbacks') is not False: for fallback in dict_inst.get('fallbacks'): fallback.pop('fallbacks')
class EnginesModelBase(AbstractConcreteBase): __tablename__ = 'engines' __swagger_json__ = get_swagger_json(__file__) _jobs = dict() __table_args__ = (sa.UniqueConstraint('name', 'store_id'), ) id = sa.Column(sa.Integer, primary_key=True) name = sa.Column(sa.String(255), nullable=False) @declared_attr def strategy_id(cls): return sa.Column(sa.ForeignKey('engine_strategies.id'), nullable=False) @declared_attr def store_id(cls): return sa.Column(sa.ForeignKey('stores.id'), nullable=False) @declared_attr def item_type_id(cls): return sa.Column(sa.ForeignKey('item_types.id'), nullable=False) @declared_attr def item_type(cls): return sa.orm.relationship('ItemTypesModel') @declared_attr def store(cls): return sa.orm.relationship('StoresModel') @declared_attr def strategy(cls): return sa.orm.relationship('EngineStrategiesModel') @declared_attr def objects(cls): return sa.orm.relationship('EngineObjectsModel', uselist=True, secondary='engines_objects') @property def variables(self): return self.strategy_instance.get_variables() @property def strategy_instance(self): if not hasattr(self, '_strategy_instance'): self._strategy_instance = \ type(self.strategy).get_instance(self._todict_when_new()) return self._strategy_instance def _todict_when_new(self): objects = [] self_dict = self.todict({'variables': False}) strategy_dict = self_dict['strategy'] for obj in self.objects: obj_dict = obj.todict() obj_dict['strategy'] = strategy_dict objects.append(obj_dict) self_dict['objects'] = objects return self_dict async def _setattr(self, attr_name, value, session, input_): if attr_name == 'item_type_id': value = {'id': value} attr_name = 'item_type' if attr_name == 'strategy_id': value = {'id': value} attr_name = 'strategy' if attr_name == 'store_id': value = {'id': value} attr_name = 'store' await super()._setattr(attr_name, value, session, input_) async def _validate(self, session, input_): strategy_class = self.strategy.get_class() for obj in self.objects: if obj.type not in strategy_class.object_types: raise SwaggerItModelError("Invalid object type '{}'".format( obj.type), instance=input_) props_sequence = [('strategy_id', self.strategy.id), ('item_type_id', self.item_type.id), ('store_id', self.store.id)] for obj in self.objects: for obj_prop_name, self_prop_value in props_sequence: value = getattr(obj, obj_prop_name) if value is None: setattr(obj, obj_prop_name, self_prop_value) elif value != self_prop_value: raise SwaggerItModelError( "Invalid object '{}' with value '{}'. "\ "This value must be the same as '{}'".format( obj_prop_name, value, self_prop_value ), instance=input_ ) self.strategy_instance.validate_config() async def init(self, session, input_=None, **kwargs): for object_ in kwargs.get('objects', []): object_['item_type_id'] = kwargs.get('item_type_id', self.item_type_id) object_['strategy_id'] = kwargs.get('strategy_id', self.strategy_id) object_['store_id'] = kwargs.get('store_id', self.store_id) await super().init(session, input_=input_, **kwargs) def _format_output_json(self, dict_inst, schema): if schema.get('variables') is not False: dict_inst['variables'] = self.variables
class UsersModelBase(AbstractConcreteBase): __tablename__ = 'users' __swagger_json__ = get_swagger_json(__file__) id = sa.Column(sa.String(255), primary_key=True) name = sa.Column(sa.String(255), unique=True, nullable=False) email = sa.Column(sa.String(255), unique=True, nullable=False) password = sa.Column(sa.String(255), nullable=False) admin = sa.Column(sa.Boolean, default=False) @declared_attr def grants(cls): return sa.orm.relationship('GrantsModel', uselist=True, secondary='users_grants') @declared_attr def stores(cls): return sa.orm.relationship('StoresModel', uselist=True, secondary='users_stores') @classmethod async def authorize(cls, session, authorization, url, method): try: authorization = b64decode(authorization).decode() except binascii.Error: return None except UnicodeDecodeError: return None if not ':' in authorization: return None user = await cls.get(session, {'id': authorization}) user = user[0] if user else user if user and user.get('admin'): session.user = user return True elif user: if method == 'OPTIONS': return True for grant in user['grants']: grant_uri = grant['uri']['uri'] if (grant_uri == url or re.match(grant_uri, url)) \ and grant['method']['method'].lower() == method.lower(): session.user = user return True return False @classmethod async def insert(cls, session, objs, commit=True, todict=True): objs = cls._to_list(objs) await cls._set_objs_ids_and_grant(objs, session) return await type(cls).insert(cls, session, objs, commit, todict) @classmethod async def _set_objs_ids_and_grant(cls, objs, session): objs = cls._to_list(objs) patch_method = await cls.get_model('methods').get( session, ids={'method': 'patch'}, todict=False) if not patch_method: patch_method = await cls.get_model('methods').insert( session, [{ 'method': 'patch' }], todict=False) patch_method = patch_method[0] get_method = await cls.get_model('methods').get(session, ids={'method': 'get'}, todict=False) if not get_method: get_method = await cls.get_model('methods').insert( session, [{ 'method': 'get' }], todict=False) get_method = get_method[0] for obj in objs: new_grants = [] user_uri = '/users/{}'.format(obj['email']) uri = await cls.get_model('uris').get(session, ids={'uri': user_uri}, todict=False) if not uri: uri = await cls.get_model('uris').insert(session, [{ 'uri': user_uri }], todict=False) uri = uri[0] grant = await cls.get_model('grants').get( session, { 'uri_id': uri.id, 'method_id': patch_method.id }, todict=False) if grant: grant = grant[0].todict() else: grant = { 'uri_id': uri.id, 'method_id': patch_method.id, '_operation': 'insert' } new_grants.append(grant) grant = await cls.get_model('grants').get( session, { 'uri_id': uri.id, 'method_id': get_method.id }, todict=False) if grant: grant = grant[0].todict() else: grant = { 'uri_id': uri.id, 'method_id': get_method.id, '_operation': 'insert' } new_grants.append(grant) obj['id'] = '{}:{}'.format(obj['email'], obj['password']) grants = obj.get('grants', []) grants.extend(new_grants) obj['grants'] = grants @classmethod async def update(cls, session, objs, commit=True, todict=True, ids=None, ids_keys=None): if not ids: ids = [] objs = cls._to_list(objs) for obj in objs: id_ = obj.get('id') email = obj.get('email') if id_ is not None: ids.append({'id': id_}) ids_keys = ('id', ) elif email is not None: ids.append({'email': email}) ids_keys = ('email', ) insts = await type(cls).update(cls, session, objs, commit=False, todict=False, ids=ids, ids_keys=ids_keys) cls._set_insts_ids(insts) if commit: await session.commit() return cls._build_todict_list(insts) if todict else insts @classmethod def _set_insts_ids(cls, insts): insts = cls._to_list(insts) for inst in insts: inst.id = '{}:{}'.format(inst.email, inst.password)
class MethodsModelBase(AbstractConcreteBase): __tablename__ = 'methods' __swagger_json__ = get_swagger_json(__file__, 'methods_swagger.json') id = sa.Column(sa.Integer, primary_key=True) method = sa.Column(sa.String(10), unique=True, nullable=False)
class URIsModelBase(AbstractConcreteBase): __tablename__ = 'uris' __swagger_json__ = get_swagger_json(__file__, 'uris_swagger.json') id = sa.Column(sa.Integer, primary_key=True) uri = sa.Column(sa.String(255), unique=True, nullable=False)
class EngineObjectsModelBase(AbstractConcreteBase): __tablename__ = 'engine_objects' __swagger_json__ = get_swagger_json(__file__) __table_args__ = (sa.UniqueConstraint('name', 'type', 'store_id'), ) id = sa.Column(sa.Integer, primary_key=True) type = sa.Column(sa.String(255), nullable=False) name = sa.Column(sa.String(255), nullable=False) configuration_json = sa.Column(sa.Text, nullable=False) @declared_attr def strategy_id(cls): return sa.Column(sa.ForeignKey('engine_strategies.id'), nullable=False) @declared_attr def store_id(cls): return sa.Column(sa.ForeignKey('stores.id'), nullable=False) @declared_attr def item_type_id(cls): return sa.Column(sa.ForeignKey('item_types.id'), nullable=False) @declared_attr def strategy(cls): return sa.orm.relationship('EngineStrategiesModel') @declared_attr def item_type(cls): return sa.orm.relationship('ItemTypesModel') @declared_attr def store(cls): return sa.orm.relationship('StoresModel') @property def configuration(self): if not hasattr(self, '_configuration'): self._configuration = ujson.loads(self.configuration_json) return self._configuration async def _setattr(self, attr_name, value, session, input_): if attr_name == 'configuration': value = ujson.dumps(value) attr_name = 'configuration_json' if attr_name == 'strategy_id': value = {'id': value} attr_name = 'strategy' await super()._setattr(attr_name, value, session, input_) def _format_output_json(self, dict_inst, schema): if schema.get('configuration') is not False: dict_inst.pop('configuration_json') dict_inst['configuration'] = self.configuration async def _validate(self, session, input_): # disable validation when the operation was did by the engine model if (isinstance(input_, list) and 'objects' in input_[0]) or \ (isinstance(input_, dict) and 'objects' in input_): return strategy_class = self.strategy.get_class() if self.type not in strategy_class.object_types: raise SwaggerItModelError("Invalid object type '{}'".format( self.type), instance=input_) object_schema = strategy_class.configuration_schema['properties'][ self.type] if 'definitions' in strategy_class.configuration_schema: object_schema['definitions'] = strategy_class.configuration_schema[ 'definitions'] jsonschema.validate(self.configuration, object_schema)
def _set_items_metaschema_route(self, swagger_doc_url): self.items_metaschema = \ get_swagger_json(__file__, 'item_types/store_items_metaschema.json') path = '/doc/items_metaschema.json' handler = self._set_handler_decorator(self._get_items_metaschema) self._set_route(path, 'GET', handler)