def put(self, **kwargs): ''' Update or create a relationship child item to be used to create or update one-to-many mappings but also works for many-to-many etc. ''' parent, child, relation = self.parse_args(**kwargs) data = request.get_json() if child and not child.id == kwargs.get('id'): raise ValidationError('ID mismatch') child = self.child_class(**data) if not child: raise ValidationError('Child Not found') relation = getattr(parent, self.rel_name ) relation.append(child) # arguments for GET : {ParentId} , {ChildId} obj_args = { self.parent_object_id : parent.id, self.child_object_id : child.id } obj_data = self.get(**obj_args) # Retrieve the object json and return it to the client response = make_response(obj_data, 201) # Set the Location header to the newly created object response.headers['Location'] = url_for(self.endpoint, **obj_args) return response
def parse_args(self, **kwargs): ''' Parse relationship args An error is raised if the parent doesn't exist. An error is raised if the child doesn't exist and the "require_child" argument is set in kwargs, Returns parent, child, relation ''' parent_id = kwargs.get(self.parent_object_id,'') parent = self.parent_class.get_instance(parent_id) if not parent: raise ValidationError('Invalid Parent Id') child = None child_id = kwargs.get(self.child_object_id,None) if child_id != None: child = self.child_class.get_instance(child_id) if not child and kwargs.get('require_child', False): raise ValidationError('Invalid Child Id') relation = getattr(parent, self.rel_name ) return parent, child, relation
def post(self, **kwargs): ''' HTTP POST: apply actions Retrieves objects from the DB based on a given query filter (in POST data) Returns a dictionary usable by jquery-bootgrid ''' id = kwargs.get(self.object_id, None) method_name = kwargs.get('method_name','') json_data = request.get_json() if not method_name: method_name = json_data.get('method',None) args = json_data.get('args') if json_data else dict(request.args) if not id: id = request.args.get('id') if id: instance = self.SAFRSObject.get_instance(id) if not instance: # If no instance was found this means the user supplied # an invalid ID raise ValidationError('Invalid ID') else: # No ID was supplied, apply method to the class itself instance = self.SAFRSObject if method_name: # call the method specified by method_name method_result = self.call_method_by_name(instance, method_name, args) result = { 'result' : method_result } return jsonify(result) # No id given, return all instances matching the filter try: filter = json_data.get('filter',{}) sort = json_data.get('sort', '' ) current = int(json_data.get('current',0)) row_count = int(json_data.get('rowCount',50)) search = json_data.get('searchPhrase','') except: raise ValidationError('Invalid arguments') instances = self.get_instances(filter, sort, search) if current < 0 : current = 1 result = { 'current' : current, 'rows' : instances[ current : current + row_count ], 'rowCount' : row_count, 'total' : instances.count() } return jsonify( result )
def get(self, **kwargs): ''' HTTP GET: return instances If no id is given: return all instances If an id is given, get an instance by id If a method is given, call the method on the instance ''' id = kwargs.get(self.object_id,None) #method_name = kwargs.get('method_name','') if not id: # If no id is given, check if it's passed through a request arg id = request.args.get('id') if id: instance = self.SAFRSObject.get_instance(id) if not instance: raise ValidationError('Invalid {}'.format(self.object_id)) # Call the method if it doesn't exist, return instance :) #method = getattr(instance, method_name, lambda : instance) #result = { 'result' : method() } result = instance else: instances = self.SAFRSObject.query.all() details = request.args.get('details',None) if details == None: result = [ item.id for item in instances ] log.debug(result) else: result = [ item for item in instances ] return jsonify(result)
def call_method_by_name(self, instance, method_name, args): ''' Call the instance method specified by method_name ''' method = getattr(instance, method_name, False) if not method: # Only call methods for Campaign and not for superclasses (e.g. db.Model) raise ValidationError('Invalid method "{}"'.format(method_name)) if not is_public(method): raise ValidationError('Method is not public') if not args: args = {} result = method(**args) return result
def delete(self, **kwargs): ''' Delete an object by id or by filter ''' id = kwargs.get(self.object_id, None) filter = {} if id: filter = dict(id = id) else: json_data = request.get_json() if json_data: filter = json_data.get('filter', {} ) if not filter: raise ValidationError('Invalid ID or Filter {} {}'.format(kwargs,self.object_id)) for instance in self.SAFRSObject.query.filter_by(**filter).all(): db.session.delete(instance) db.session.commit() return jsonify({}) , 204
def add_resource(self, resource, *urls, **kwargs): ''' This method is partly copied from flask_restful_swagger_2/__init__.py I changed it because we don't need path id examples when there's no {id} in the path. We filter out the unwanted parameters ''' from flask_restful_swagger_2 import validate_definitions_object, parse_method_doc from flask_restful_swagger_2 import validate_path_item_object, extract_swagger_path path_item = {} definitions = {} resource_methods = kwargs.get('methods',['GET','PUT','POST','DELETE']) for method in [m.lower() for m in resource.methods]: if not method.upper() in resource_methods: continue f = resource.__dict__.get(method, None) if not f: continue operation = f.__dict__.get('__swagger_operation_object', None) if operation: operation, definitions_ = self._extract_schemas(operation) path_item[method] = operation definitions.update(definitions_) summary = parse_method_doc(f, operation) if summary: operation['summary'] = summary validate_definitions_object(definitions) self._swagger_object['definitions'].update(definitions) if path_item: validate_path_item_object(path_item) for url in urls: if not url.startswith('/'): raise ValidationError('paths must start with a /') swagger_url = extract_swagger_path(url) for method in [m.lower() for m in resource.methods]: method_doc = copy.deepcopy(path_item.get(method)) if not method_doc: continue filtered_parameters = [] for parameter in method_doc.get('parameters',[]): object_id = '{%s}'%parameter.get('name') if method == 'get' and not swagger_url.endswith('Id}') : param = {'default': 'all', 'type': 'string', 'name': 'details', 'in': 'query'} if not param in filtered_parameters: filtered_parameters.append(param) if method == 'post' and not swagger_url.endswith('Id}') and not parameter.get('description','').endswith('(classmethod)'): # Only classmethods should be added when there's no {id} in the POST path for this method continue if not ( parameter.get('in') == 'path' and not object_id in swagger_url ): # Only if a path param is in path url then we add the param filtered_parameters.append(parameter) #log.debug(method_doc) method_doc['parameters'] = filtered_parameters path_item[method] = method_doc if method == 'get' and not swagger_url.endswith('Id}'): # If no {id} was provided, we return a list of all the objects try: method_doc['description'] += ' list (See GET /{id} for details)' method_doc['responses']['200']['schema'] = '' except: pass self._swagger_object['paths'][swagger_url] = path_item self._swagger_object['securityDefinitions'] = { "api_key": { "type": "apiKey", "name": "api_key", "in": "query" }} self._swagger_object['security'] = [ "api_key" ] super(ApiBase, self).add_resource(resource, *urls, **kwargs)