txerpa /
besepa-python
| 1 | import besepa.util as util |
||
| 2 | from besepa.api import default as default_api |
||
| 3 | |||
| 4 | |||
| 5 | class Resource(object): |
||
| 6 | """Base class for all REST services |
||
| 7 | """ |
||
| 8 | convert_resources = {} |
||
| 9 | |||
| 10 | def __init__(self, attributes=None, api=None): |
||
| 11 | attributes = attributes or {} |
||
| 12 | self.__dict__['api'] = api or default_api() |
||
| 13 | |||
| 14 | super(Resource, self).__setattr__('__data__', {}) |
||
| 15 | super(Resource, self).__setattr__('error', None) |
||
| 16 | super(Resource, self).__setattr__('headers', {}) |
||
| 17 | super(Resource, self).__setattr__('header', {}) |
||
| 18 | self.merge(attributes) |
||
| 19 | |||
| 20 | def http_headers(self): |
||
| 21 | """Generate HTTP header |
||
| 22 | """ |
||
| 23 | return util.merge_dict(self.header, self.headers) |
||
| 24 | |||
| 25 | def __str__(self): |
||
| 26 | return self.__data__.__str__() |
||
| 27 | |||
| 28 | def __repr__(self): |
||
| 29 | return self.__data__.__str__() |
||
| 30 | |||
| 31 | def __getattr__(self, name): |
||
| 32 | return self.__data__.get(name) |
||
| 33 | |||
| 34 | def __setattr__(self, name, value): |
||
| 35 | try: |
||
| 36 | # Handle attributes(error, header) |
||
| 37 | super(Resource, self).__getattribute__(name) |
||
| 38 | super(Resource, self).__setattr__(name, value) |
||
| 39 | except AttributeError: |
||
| 40 | self.__data__[name] = self.convert(name, value) |
||
| 41 | |||
| 42 | def __contains__(self, item): |
||
| 43 | return item in self.__data__ |
||
| 44 | |||
| 45 | def success(self): |
||
| 46 | return self.error is None |
||
| 47 | |||
| 48 | def merge(self, new_attributes): |
||
| 49 | """Merge new attributes e.g. response from a post to Resource |
||
| 50 | """ |
||
| 51 | for k, v in new_attributes.items(): |
||
| 52 | setattr(self, k, v) |
||
| 53 | |||
| 54 | def convert(self, name, value): |
||
| 55 | """Convert the attribute values to configured class |
||
| 56 | """ |
||
| 57 | if isinstance(value, dict): |
||
|
0 ignored issues
–
show
unused-code
introduced
by
Loading history...
|
|||
| 58 | cls = self.convert_resources.get(name, Resource) |
||
| 59 | return cls(value, api=self.api) |
||
| 60 | elif isinstance(value, list): |
||
| 61 | new_list = [] |
||
| 62 | for obj in value: |
||
| 63 | new_list.append(self.convert(name, obj)) |
||
| 64 | return new_list |
||
| 65 | else: |
||
| 66 | return value |
||
| 67 | |||
| 68 | def __getitem__(self, key): |
||
| 69 | return self.__data__[key] |
||
| 70 | |||
| 71 | def __setitem__(self, key, value): |
||
| 72 | self.__data__[key] = self.convert(key, value) |
||
| 73 | |||
| 74 | def to_dict(self): |
||
| 75 | |||
| 76 | def parse_object(value): |
||
| 77 | if isinstance(value, Resource): |
||
|
0 ignored issues
–
show
|
|||
| 78 | return value.to_dict() |
||
| 79 | elif isinstance(value, list): |
||
| 80 | return list(map(parse_object, value)) |
||
| 81 | else: |
||
| 82 | return value |
||
| 83 | |||
| 84 | return dict((key, parse_object(value)) for (key, value) in self.__data__.items()) |
||
| 85 | |||
| 86 | |||
| 87 | class Find(Resource): |
||
| 88 | |||
| 89 | @classmethod |
||
| 90 | def find(cls, resource_id, api=None): |
||
| 91 | """Locate resource e.g. customer with given id |
||
| 92 | |||
| 93 | Usage:: |
||
| 94 | >>> payment = Customer.find("1") |
||
| 95 | """ |
||
| 96 | api = api or default_api() |
||
| 97 | |||
| 98 | url = util.join_url(cls.path, str(resource_id)) |
||
| 99 | return cls(api.get(url), api=api) |
||
| 100 | |||
| 101 | |||
| 102 | class List(Resource): |
||
| 103 | |||
| 104 | list_class = Resource |
||
| 105 | |||
| 106 | @classmethod |
||
| 107 | def all(cls, params=None, api=None): |
||
| 108 | """Get list of resources |
||
| 109 | |||
| 110 | Usage:: |
||
| 111 | |||
| 112 | >>> payment_histroy = Customer.all({'per_page': 2}) |
||
| 113 | """ |
||
| 114 | api = api or default_api() |
||
| 115 | url = cls.path if params is None else util.join_url_params(cls.path, params) |
||
| 116 | |||
| 117 | try: |
||
| 118 | response = api.get(url) |
||
| 119 | return cls.list_class(response, api=api) |
||
| 120 | except AttributeError: |
||
| 121 | # To handle the case when response is JSON Array |
||
| 122 | if isinstance(response, list): |
||
| 123 | new_resp = [cls.list_class(elem, api=api) for elem in response] |
||
| 124 | return new_resp |
||
| 125 | |||
| 126 | |||
| 127 | class Create(Resource): |
||
| 128 | |||
| 129 | def create(self): |
||
| 130 | """Creates a resource e.g. payment |
||
| 131 | |||
| 132 | Usage:: |
||
| 133 | |||
| 134 | >>> customer = Customer({}) |
||
| 135 | >>> customer.create() # return True or False |
||
| 136 | """ |
||
| 137 | resource_name = self.__class__.__name__.lower() |
||
| 138 | payload = {resource_name: self.to_dict()} |
||
| 139 | new_attributes = self.api.post(self.path, payload, self.http_headers()) |
||
| 140 | self.error = None |
||
| 141 | self.merge(new_attributes) |
||
| 142 | return self.success() |
||
| 143 | |||
| 144 | |||
| 145 | class Update(Resource): |
||
| 146 | """Partial update or modify resource |
||
| 147 | |||
| 148 | Usage:: |
||
| 149 | |||
| 150 | >>> customer.update([{'name': 'Andrew'}]) |
||
| 151 | """ |
||
| 152 | |||
| 153 | def update(self, attributes=None): |
||
| 154 | attributes = attributes or self.to_dict() |
||
| 155 | url = util.join_url(self.path, str(self['id'])) |
||
| 156 | new_attributes = self.api.patch(url, attributes, self.http_headers()) |
||
| 157 | self.error = None |
||
| 158 | self.merge(new_attributes) |
||
| 159 | return self.success() |
||
| 160 | |||
| 161 | |||
| 162 | class Delete(Resource): |
||
| 163 | |||
| 164 | def delete(self): |
||
| 165 | """Deletes a resource e.g. bank_account |
||
| 166 | |||
| 167 | Usage:: |
||
| 168 | |||
| 169 | >>> bank_account.delete() |
||
| 170 | """ |
||
| 171 | url = util.join_url(self.path, str(self['id'])) |
||
| 172 | new_attributes = self.api.delete(url) |
||
| 173 | self.error = None |
||
| 174 | self.merge(new_attributes) |
||
| 175 | return self.success() |
||
| 176 | |||
| 177 | |||
| 178 | class Post(Resource): |
||
| 179 | |||
| 180 | def post(self, name, attributes=None, cls=Resource, fieldname='id'): |
||
| 181 | """Constructs url with passed in headers and makes post request via |
||
| 182 | post method in api class. |
||
| 183 | |||
| 184 | Usage:: |
||
| 185 | |||
| 186 | >>> client.post("stats", {'id': '1234'}, client) # return True or False |
||
| 187 | """ |
||
| 188 | attributes = attributes or {} |
||
| 189 | url = util.join_url(self.path, str(self[fieldname]), name) |
||
| 190 | if not isinstance(attributes, Resource): |
||
| 191 | attributes = Resource(attributes, api=self.api) |
||
| 192 | new_attributes = self.api.post(url, attributes.to_dict(), attributes.http_headers()) |
||
| 193 | if isinstance(cls, Resource): |
||
|
0 ignored issues
–
show
|
|||
| 194 | cls.error = None |
||
| 195 | cls.merge(new_attributes) |
||
| 196 | return self.success() |
||
| 197 | else: |
||
| 198 | return cls(new_attributes, api=self.api) |
||
| 199 |