source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/packages/dal/pydal/helpers/rest.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 16.1 KB
Line 
1import re
2from .regex import REGEX_SEARCH_PATTERN, REGEX_SQUARE_BRACKETS
3from .._compat import long
4
5
6def to_num(num):
7    result = 0
8    try:
9        result = long(num)
10    except NameError as e:
11        result = int(num)
12    return result
13
14
15class RestParser(object):
16    def __init__(self, db):
17        self.db = db
18
19    def auto_table(self, table, base="", depth=0):
20        patterns = []
21        for field in self.db[table].fields:
22            if base:
23                tag = "%s/%s" % (base, field.replace("_", "-"))
24            else:
25                tag = "/%s/%s" % (table.replace("_", "-"), field.replace("_", "-"))
26            f = self.db[table][field]
27            if not f.readable:
28                continue
29            if f.type == "id" or "slug" in field or f.type.startswith("reference"):
30                tag += "/{%s.%s}" % (table, field)
31                patterns.append(tag)
32                patterns.append(tag + "/:field")
33            elif f.type.startswith("boolean"):
34                tag += "/{%s.%s}" % (table, field)
35                patterns.append(tag)
36                patterns.append(tag + "/:field")
37            elif f.type in ("float", "double", "integer", "bigint"):
38                tag += "/{%s.%s.ge}/{%s.%s.lt}" % (table, field, table, field)
39                patterns.append(tag)
40                patterns.append(tag + "/:field")
41            elif f.type.startswith("list:"):
42                tag += "/{%s.%s.contains}" % (table, field)
43                patterns.append(tag)
44                patterns.append(tag + "/:field")
45            elif f.type in ("date", "datetime"):
46                tag += "/{%s.%s.year}" % (table, field)
47                patterns.append(tag)
48                patterns.append(tag + "/:field")
49                tag += "/{%s.%s.month}" % (table, field)
50                patterns.append(tag)
51                patterns.append(tag + "/:field")
52                tag += "/{%s.%s.day}" % (table, field)
53                patterns.append(tag)
54                patterns.append(tag + "/:field")
55            if f.type in ("datetime", "time"):
56                tag += "/{%s.%s.hour}" % (table, field)
57                patterns.append(tag)
58                patterns.append(tag + "/:field")
59                tag += "/{%s.%s.minute}" % (table, field)
60                patterns.append(tag)
61                patterns.append(tag + "/:field")
62                tag += "/{%s.%s.second}" % (table, field)
63                patterns.append(tag)
64                patterns.append(tag + "/:field")
65            if depth > 0:
66                for f in self.db[table]._referenced_by:
67                    tag += "/%s[%s.%s]" % (table, f.tablename, f.name)
68                    patterns.append(tag)
69                    patterns += self.auto_table(table, base=tag, depth=depth - 1)
70        return patterns
71
72    def parse(self, patterns, args, vars, queries=None, nested_select=True):
73        """
74        Example:
75            Use as::
76
77                db.define_table('person',Field('name'),Field('info'))
78                db.define_table('pet',
79                    Field('ownedby',db.person),
80                    Field('name'),Field('info')
81                )
82
83                @request.restful()
84                def index():
85                    def GET(*args,**vars):
86                        patterns = [
87                            "/friends[person]",
88                            "/{person.name}/:field",
89                            "/{person.name}/pets[pet.ownedby]",
90                            "/{person.name}/pets[pet.ownedby]/{pet.name}",
91                            "/{person.name}/pets[pet.ownedby]/{pet.name}/:field",
92                            ("/dogs[pet]", db.pet.info=='dog'),
93                            ("/dogs[pet]/{pet.name.startswith}", db.pet.info=='dog'),
94                            ]
95                        parser = db.parse_as_rest(patterns,args,vars)
96                        if parser.status == 200:
97                            return dict(content=parser.response)
98                        else:
99                            raise HTTP(parser.status,parser.error)
100
101                    def POST(table_name,**vars):
102                        if table_name == 'person':
103                            return db.person.validate_and_insert(**vars)
104                        elif table_name == 'pet':
105                            return db.pet.validate_and_insert(**vars)
106                        else:
107                            raise HTTP(400)
108                    return locals()
109        """
110
111        if patterns == "auto":
112            patterns = []
113            for table in self.db.tables:
114                if not table.startswith("auth_"):
115                    patterns.append("/%s[%s]" % (table, table))
116                    patterns += self.auto_table(table, base="", depth=1)
117        else:
118            i = 0
119            while i < len(patterns):
120                pattern = patterns[i]
121                if not isinstance(pattern, str):
122                    pattern = pattern[0]
123                tokens = pattern.split("/")
124                if tokens[-1].startswith(":auto") and re.match(
125                    REGEX_SQUARE_BRACKETS, tokens[-1]
126                ):
127                    new_patterns = self.auto_table(
128                        tokens[-1][tokens[-1].find("[") + 1 : -1], "/".join(tokens[:-1])
129                    )
130                    patterns = patterns[:i] + new_patterns + patterns[i + 1 :]
131                    i += len(new_patterns)
132                else:
133                    i += 1
134        if "/".join(args) == "patterns":
135            return self.db.Row(
136                {"status": 200, "pattern": "list", "error": None, "response": patterns}
137            )
138        for pattern in patterns:
139            basequery, exposedfields = None, []
140            if isinstance(pattern, tuple):
141                if len(pattern) == 2:
142                    pattern, basequery = pattern
143                elif len(pattern) > 2:
144                    pattern, basequery, exposedfields = pattern[0:3]
145            otable = table = None
146            if not isinstance(queries, dict):
147                dbset = self.db(queries)
148                if basequery is not None:
149                    dbset = dbset(basequery)
150            i = 0
151            tags = pattern[1:].split("/")
152            if len(tags) != len(args):
153                continue
154            for tag in tags:
155                if re.match(REGEX_SEARCH_PATTERN, tag):
156                    tokens = tag[1:-1].split(".")
157                    table, field = tokens[0], tokens[1]
158                    if not otable or table == otable:
159                        if len(tokens) == 2 or tokens[2] == "eq":
160                            query = self.db[table][field] == args[i]
161                        elif tokens[2] == "ne":
162                            query = self.db[table][field] != args[i]
163                        elif tokens[2] == "lt":
164                            query = self.db[table][field] < args[i]
165                        elif tokens[2] == "gt":
166                            query = self.db[table][field] > args[i]
167                        elif tokens[2] == "ge":
168                            query = self.db[table][field] >= args[i]
169                        elif tokens[2] == "le":
170                            query = self.db[table][field] <= args[i]
171                        elif tokens[2] == "year":
172                            query = self.db[table][field].year() == args[i]
173                        elif tokens[2] == "month":
174                            query = self.db[table][field].month() == args[i]
175                        elif tokens[2] == "day":
176                            query = self.db[table][field].day() == args[i]
177                        elif tokens[2] == "hour":
178                            query = self.db[table][field].hour() == args[i]
179                        elif tokens[2] == "minute":
180                            query = self.db[table][field].minutes() == args[i]
181                        elif tokens[2] == "second":
182                            query = self.db[table][field].seconds() == args[i]
183                        elif tokens[2] == "startswith":
184                            query = self.db[table][field].startswith(args[i])
185                        elif tokens[2] == "contains":
186                            query = self.db[table][field].contains(args[i])
187                        else:
188                            raise RuntimeError("invalid pattern: %s" % pattern)
189                        if len(tokens) == 4 and tokens[3] == "not":
190                            query = ~query
191                        elif len(tokens) >= 4:
192                            raise RuntimeError("invalid pattern: %s" % pattern)
193                        if not otable and isinstance(queries, dict):
194                            dbset = self.db(queries[table])
195                            if basequery is not None:
196                                dbset = dbset(basequery)
197                        dbset = dbset(query)
198                    else:
199                        raise RuntimeError("missing relation in pattern: %s" % pattern)
200                elif (
201                    re.match(REGEX_SQUARE_BRACKETS, tag)
202                    and args[i] == tag[: tag.find("[")]
203                ):
204                    ref = tag[tag.find("[") + 1 : -1]
205                    if "." in ref and otable:
206                        table, field = ref.split(".")
207                        selfld = "_id"
208                        if self.db[table][field].type.startswith("reference "):
209                            refs = [
210                                x.name
211                                for x in self.db[otable]
212                                if x.type == self.db[table][field].type
213                            ]
214                        else:
215                            refs = [
216                                x.name
217                                for x in self.db[table]._referenced_by
218                                if x.tablename == otable
219                            ]
220                        if refs:
221                            selfld = refs[0]
222                        if nested_select:
223                            try:
224                                dbset = self.db(
225                                    self.db[table][field].belongs(
226                                        dbset._select(self.db[otable][selfld])
227                                    )
228                                )
229                            except ValueError:
230                                return self.db.Row(
231                                    {
232                                        "status": 400,
233                                        "pattern": pattern,
234                                        "error": "invalid path",
235                                        "response": None,
236                                    }
237                                )
238                        else:
239                            items = [
240                                item.id
241                                for item in dbset.select(self.db[otable][selfld])
242                            ]
243                            dbset = self.db(self.db[table][field].belongs(items))
244                    else:
245                        table = ref
246                        if not otable and isinstance(queries, dict):
247                            dbset = self.db(queries[table])
248                        dbset = dbset(self.db[table])
249                elif tag == ":field" and table:
250                    # print 're3:'+tag
251                    field = args[i]
252                    if field not in self.db[table]:
253                        break
254                    # hand-built patterns should respect .readable=False as well
255                    if not self.db[table][field].readable:
256                        return self.db.Row(
257                            {
258                                "status": 418,
259                                "pattern": pattern,
260                                "error": "I'm a teapot",
261                                "response": None,
262                            }
263                        )
264                    try:
265                        distinct = vars.get("distinct", False) == "True"
266                        offset = to_num(vars.get("offset", None) or 0)
267                        limits = (
268                            offset,
269                            to_num(vars.get("limit", None) or 1000) + offset,
270                        )
271                    except ValueError:
272                        return self.db.Row(
273                            {"status": 400, "error": "invalid limits", "response": None}
274                        )
275                    items = dbset.select(
276                        self.db[table][field], distinct=distinct, limitby=limits
277                    )
278                    if items:
279                        return self.db.Row(
280                            {"status": 200, "response": items, "pattern": pattern}
281                        )
282                    else:
283                        return self.db.Row(
284                            {
285                                "status": 404,
286                                "pattern": pattern,
287                                "error": "no record found",
288                                " response": None,
289                            }
290                        )
291                elif tag != args[i]:
292                    break
293                otable = table
294                i += 1
295                if i == len(tags) and table:
296                    if hasattr(self.db[table], "_id"):
297                        ofields = vars.get("order", self.db[table]._id.name).split("|")
298                    else:
299                        ofields = vars.get(
300                            "order", self.db[table]._primarykey[0]
301                        ).split("|")
302                    try:
303                        orderby = [
304                            self.db[table][f]
305                            if not f.startswith("~")
306                            else ~self.db[table][f[1:]]
307                            for f in ofields
308                        ]
309                    except (KeyError, AttributeError):
310                        return self.db.Row(
311                            {
312                                "status": 400,
313                                "error": "invalid orderby",
314                                "response": None,
315                            }
316                        )
317                    if exposedfields:
318                        fields = [
319                            field
320                            for field in self.db[table]
321                            if str(field).split(".")[-1] in exposedfields
322                            and field.readable
323                        ]
324                    else:
325                        fields = [field for field in self.db[table] if field.readable]
326                    count = dbset.count()
327                    try:
328                        offset = to_num(vars.get("offset", None) or 0)
329                        limits = (
330                            offset,
331                            to_num(vars.get("limit", None) or 1000) + offset,
332                        )
333                    except ValueError:
334                        return self.db.Row(
335                            {
336                                "status": 400,
337                                "error": " invalid limits",
338                                "response": None,
339                            }
340                        )
341                    try:
342                        response = dbset.select(
343                            limitby=limits, orderby=orderby, *fields
344                        )
345                    except ValueError:
346                        return self.db.Row(
347                            {
348                                "status": 400,
349                                "pattern": pattern,
350                                "error": "invalid path",
351                                "response": None,
352                            }
353                        )
354                    return self.db.Row(
355                        {
356                            "status": 200,
357                            "response": response,
358                            "pattern": pattern,
359                            "count": count,
360                        }
361                    )
362        return self.db.Row(
363            {"status": 400, "error": "no matching pattern", "response": None}
364        )
Note: See TracBrowser for help on using the repository browser.