Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/flask_restx/resource.py: 23%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

47 statements  

1from flask import request 

2from flask.views import MethodView 

3 

4 

5from .model import ModelBase 

6 

7from .utils import unpack, BaseResponse 

8 

9 

10class Resource(MethodView): 

11 """ 

12 Represents an abstract RESTX resource. 

13 

14 Concrete resources should extend from this class 

15 and expose methods for each supported HTTP method. 

16 If a resource is invoked with an unsupported HTTP method, 

17 the API will return a response with status 405 Method Not Allowed. 

18 Otherwise the appropriate method is called and passed all arguments 

19 from the url rule used when adding the resource to an Api instance. 

20 See :meth:`~flask_restx.Api.add_resource` for details. 

21 """ 

22 

23 representations = None 

24 method_decorators = [] 

25 

26 def __init__(self, api=None, *args, **kwargs): 

27 self.api = api 

28 

29 def dispatch_request(self, *args, **kwargs): 

30 # Taken from flask 

31 meth = getattr(self, request.method.lower(), None) 

32 if meth is None and request.method == "HEAD": 

33 meth = getattr(self, "get", None) 

34 assert meth is not None, "Unimplemented method %r" % request.method 

35 

36 for decorator in self.method_decorators: 

37 meth = decorator(meth) 

38 

39 self.validate_payload(meth) 

40 

41 resp = meth(*args, **kwargs) 

42 

43 if isinstance(resp, BaseResponse): 

44 return resp 

45 

46 representations = self.representations or {} 

47 

48 mediatype = request.accept_mimetypes.best_match(representations, default=None) 

49 if mediatype in representations: 

50 data, code, headers = unpack(resp) 

51 resp = representations[mediatype](data, code, headers) 

52 resp.headers["Content-Type"] = mediatype 

53 return resp 

54 

55 return resp 

56 

57 def __validate_payload(self, expect, collection=False): 

58 """ 

59 :param ModelBase expect: the expected model for the input payload 

60 :param bool collection: False if a single object of a resource is 

61 expected, True if a collection of objects of a resource is expected. 

62 """ 

63 # TODO: proper content negotiation 

64 data = request.get_json() 

65 if collection: 

66 data = data if isinstance(data, list) else [data] 

67 for obj in data: 

68 expect.validate(obj, self.api.refresolver, self.api.format_checker) 

69 else: 

70 expect.validate(data, self.api.refresolver, self.api.format_checker) 

71 

72 def validate_payload(self, func): 

73 """Perform a payload validation on expected model if necessary""" 

74 if getattr(func, "__apidoc__", False) is not False: 

75 doc = func.__apidoc__ 

76 validate = doc.get("validate", None) 

77 validate = validate if validate is not None else self.api._validate 

78 if validate: 

79 for expect in doc.get("expect", []): 

80 # TODO: handle third party handlers 

81 if isinstance(expect, list) and len(expect) == 1: 

82 if isinstance(expect[0], ModelBase): 

83 self.__validate_payload(expect[0], collection=True) 

84 if isinstance(expect, ModelBase): 

85 self.__validate_payload(expect, collection=False)