aboutsummaryrefslogtreecommitdiffstats
path: root/pygithub3/requests/base.py
blob: 03b0f8a7e93e0c386c4f3e733de0a172d327f0a2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

import re
try:
    import simplejson as json
except ImportError:
    import json

from pygithub3.exceptions import (DoesNotExists, UriInvalid, ValidationError,
                                  InvalidBodySchema)
from pygithub3.resources.base import Raw
from pygithub3.core.utils import import_module

ABS_IMPORT_PREFIX = 'pygithub3.requests'


class Body(object):

    def __init__(self, content, schema, required):
        self.content = content
        self.schema = schema
        self.required = required

    def dumps(self):
        if not self.schema:
            return self.content or None
        return json.dumps(self.parse())

    def parse(self):
        if not hasattr(self.content, 'items'):
            raise ValidationError("'%s' needs a content dictionary"
                                   % self.__class__.__name__)
        parsed = dict([(key, self.content[key]) for key in self.schema
                      if key in self.content])
        for attr_required in self.required:
            if attr_required not in parsed:
                raise ValidationError("'%s' attribute is required" %
                                      attr_required)
            if parsed[attr_required] is None:
                raise ValidationError("'%s' attribute can't be empty" %
                                      attr_required)
        return parsed


class Request(object):
    """ """

    uri = ''
    resource = Raw
    body_schema = {}

    def __init__(self, **kwargs):
        """ """
        self.body = kwargs.pop('body', None)
        self.args = kwargs
        self.clean()

    def clean(self):
        self.uri = self.clean_uri() or self.uri
        self.body = Body(self.clean_body(), **self.clean_valid_body())

    def clean_body(self):
        return self.body

    def clean_uri(self):
        return None

    def clean_valid_body(self):
        schema = set(self.body_schema.get('schema', ()))
        required = set(self.body_schema.get('required', ()))
        if not required.issubset(schema):
            raise InvalidBodySchema(
                "'%s:valid_body' attribute is invalid. "
                "'%s required' isn't a subset of '%s schema'" % (
                self.__class__.__name__, required, schema))
        return dict(schema=schema, required=required)

    def __getattr__(self, name):
        return self.args.get(name)

    def __str__(self):
        return self.populate_uri()

    def populate_uri(self):
        try:
            populated_uri = self.uri.format(**self.args)
        except KeyError:
            raise ValidationError(
                "'%s' request wasn't be able to populate the uri '%s' with "
                "'%s' args" % (self.__class__.__name__, self.uri, self.args))
        return str(populated_uri).strip('/')

    def get_body(self):
        return self.body.dumps()


class Factory(object):
    """ Request builder """

    import_pattern = re.compile(r'^(\w+\.)+\w+$')

    def validate(func):
        """ Decorator to check if request_uri
        has valid format: 'from.path.module.class' """

        def wrapper(self, request_uri, **kwargs):
            if not Factory.import_pattern.match(request_uri):
                raise UriInvalid("'%s' isn't valid form" % request_uri)
            return func(self, request_uri.lower(), **kwargs)
        return wrapper

    def dispatch(func):
        def wrapper(self, request_uri, **kwargs):
            module_chunk, s, request_chunk = request_uri.rpartition('.')
            request_chunk = request_chunk.capitalize()
            try:
                #  TODO: CamelCase and under_score support, now only Class Name
                module = import_module('%s.%s'
                                        % (ABS_IMPORT_PREFIX, module_chunk))
                request = getattr(module, request_chunk)
            except ImportError:
                raise DoesNotExists("'%s' module does not exists"
                                       % module_chunk)
            except AttributeError:
                raise DoesNotExists(
                    "'%s' request doesn't exists into '%s' module"
                    % (request_chunk, module_chunk))
            return func(self, request, **kwargs)
        return wrapper

    @validate
    @dispatch
    def __call__(self, request='', **kwargs):
        request = request(**kwargs)
        assert isinstance(request, Request)
        return request