buildbot / buildbot

@@ -0,0 +1,106 @@
Loading
1 +
# This file is part of Buildbot.  Buildbot is free software: you can
2 +
# redistribute it and/or modify it under the terms of the GNU General Public
3 +
# License as published by the Free Software Foundation, version 2.
4 +
#
5 +
# This program is distributed in the hope that it will be useful, but WITHOUT
6 +
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 +
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8 +
# details.
9 +
#
10 +
# You should have received a copy of the GNU General Public License along with
11 +
# this program; if not, write to the Free Software Foundation, Inc., 51
12 +
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13 +
#
14 +
# Copyright Buildbot Team Members
15 +
16 +
import json
17 +
18 +
from twisted.internet import defer
19 +
from twisted.python import log
20 +
from twisted.web.error import Error
21 +
22 +
from buildbot.util import bytes2unicode
23 +
from buildbot.util import unicode2bytes
24 +
from buildbot.www import resource
25 +
from buildbot.www.rest import RestRootResource
26 +
27 +
28 +
class V3RootResource(resource.Resource):
29 +
    isLeaf = True
30 +
31 +
    # enable reconfigResource calls
32 +
    needsReconfig = True
33 +
34 +
    def reconfigResource(self, new_config):
35 +
        # @todo v2 has cross origin support, which might need to be factorized
36 +
        self.gql_config = new_config.www.get("graphql")
37 +
        self.debug = True
38 +
        self.graphql = None
39 +
        if self.gql_config is not None:
40 +
            try:
41 +
                import graphql
42 +
            except ImportError:  # pragma: no cover
43 +
                raise ImportError(
44 +
                    "graphql is enabled but 'graphql-core' is not installed"
45 +
                )
46 +
            self.graphql = graphql
47 +
            self.debug = self.gql_config.get("debug")
48 +
            self.schema = graphql.build_schema(self.master.data.get_graphql_schema())
49 +
50 +
    def render(self, request):
51 +
        def writeError(msg, errcode=400):
52 +
            if isinstance(msg, list):
53 +
                errors = msg
54 +
            else:
55 +
                msg = bytes2unicode(msg)
56 +
                errors = [{"message": msg}]
57 +
            if self.debug:
58 +
                log.msg("HTTP error: {}".format(errors))
59 +
            request.setResponseCode(errcode)
60 +
            request.setHeader(b"content-type", b"application/json; charset=utf-8")
61 +
            data = json.dumps({"data": None, "errors": errors})
62 +
            data = unicode2bytes(data)
63 +
            request.write(data)
64 +
            request.finish()
65 +
66 +
        return self.asyncRenderHelper(request, self.asyncRender, writeError)
67 +
68 +
    def renderQuery(self, query):
69 +
        query = self.graphql.parse(query)
70 +
        errors = self.graphql.validate(self.schema, query)
71 +
        if errors:
72 +
            raise Error(400, [e.formatted for e in errors])
73 +
74 +
    @defer.inlineCallbacks
75 +
    def asyncRender(self, request):
76 +
        if self.graphql is None:
77 +
            raise Error(501, "graphql not enabled")
78 +
79 +
        # graphql accepts its query either in post data or get query
80 +
        if request.method == b"POST":
81 +
            content_type = request.getHeader(b"content-type")
82 +
            if content_type == b"application/graphql":
83 +
                query = request.content.read().decode()
84 +
            elif content_type == b"application/json":
85 +
                json_query = json.load(request.content)
86 +
                query = json_query.pop('query')
87 +
                if json_query:
88 +
                    fields = " ".join(json_query.keys())
89 +
                    raise Error(400, b"json request unsupported fields: " + fields.encode())
90 +
            elif content_type is None:
91 +
                raise Error(400, b"no content-type")
92 +
            else:
93 +
                raise Error(400, b"unsupported content-type: " + content_type)
94 +
95 +
        elif request.method in (b"GET"):
96 +
            if b"query" not in request.args:
97 +
                raise Error(400, b"GET request must contain a 'query' parameter")
98 +
            query = request.args[b"query"][0].decode()
99 +
        else:
100 +
            raise Error(400, b"invalid HTTP method")
101 +
102 +
        res = yield self.renderQuery(query)
103 +
        return res
104 +
105 +
106 +
RestRootResource.addApiVersion(3, V3RootResource)
Files Coverage
master/buildbot 92.25%
worker/buildbot_worker 85.54%
Project Totals (341 files) 91.84%
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading