Esempio n. 1
0
class HTTPService(CodeBlock):
    """
    Combines sphinxcontrib.httpdomain and sphinxcontrib.httpexample to
    interpret guillotina configuration and automatically generate documentation
    blocks for services
    """

    required_arguments = 0
    option_spec = httpex_utils.merge_dicts(CodeBlock.option_spec, {
        'type': directives.unchanged,
        'directory': directives.unchanged
    })

    def __init__(self, *args, **kwargs):
        super(HTTPService, self).__init__(*args, **kwargs)
        cwd = os.path.dirname(self.state.document.current_source)
        self.dir = os.path.normpath(
            os.path.join(cwd, self.options['directory']))

    def get_json_from_file(self, filename):
        fi = open(filename, 'r')
        data = json.loads(fi.read())
        fi.close()
        return data

    def write_tmp(self, data):
        _, filename = tempfile.mkstemp()
        fi = open(filename, 'w')
        fi.write(data)
        fi.close()
        return filename

    def process_service(self, filename):
        data = self.get_json_from_file(os.path.join(self.dir, filename))
        request_filename = self.write_tmp(data['request'] or '')
        response_filename = self.write_tmp(data['response'] or '')

        example = HTTPExample('http:example',
                              arguments=['curl', 'httpie', 'python-requests'],
                              options={
                                  'request': request_filename,
                                  'response': response_filename
                              },
                              content=self.content,
                              lineno=self.lineno,
                              content_offset=self.content_offset,
                              block_text='.. http:example::',
                              state=self.state,
                              state_machine=self.state_machine)

        method = data['method'].upper()
        service = data['service']
        name = service.get('name') or ''
        path_scheme = data.get('path_scheme') or name
        summary = service.get('summary') or ''
        permission = service.get('permission') or ''

        container = nodes.container('')
        container.append(addnodes.desc_name('', method + ' '))
        container.append(addnodes.desc_name('', path_scheme))

        inner_container = nodes.definition('')
        container.append(inner_container)

        inner_container.append(nodes.paragraph(summary, summary))
        inner_container.append(addnodes.desc_name('permission', 'permission'))
        perm_label = ': ' + permission
        inner_container.append(addnodes.desc_annotation(
            perm_label, perm_label))
        inner_container.append(example.run()[0])

        # extra = nodes.paragraph('', '')
        # inner_container.append(extra)
        # if service.get('responses'):
        #     extra.append(nodes.strong('', 'Responses'))
        #     blist = nodes.bullet_list('')
        #     extra.append(blist)
        #     for code, config in service['responses'].items():
        #         blist.append(render_response(code, 'Hello'))

        # cleanup
        os.remove(request_filename)
        os.remove(response_filename)

        return container

    def run(self):
        type_name = self.options['type']
        files = []
        for filename in os.listdir(self.dir):
            if filename.startswith(type_name + '-'):
                files.append(filename)
        files.sort(key=service_filename_sort_key)

        env = self.state.document.settings.env
        targetid = "service-%d" % env.new_serialno('service')
        targetnode = nodes.target('', '', ids=[targetid])

        return [targetnode
                ] + [self.process_service(filename) for filename in files]
class HTTPExample(CodeBlock):

    required_arguments = 0
    optional_arguments = len(AVAILABLE_BUILDERS)

    option_spec = utils.merge_dicts(CodeBlock.option_spec, {
        'request': directives.unchanged,
        'response': directives.unchanged,
    })

    def run(self):
        config = self.state.document.settings.env.config

        # Read enabled builders; Defaults to None
        if self.arguments:
            chosen_builders = choose_builders(self.arguments)
        else:
            chosen_builders = []

        # Enable 'http' language for http part
        self.arguments = ['http']

        # Optionally, read directive content from 'request' option
        cwd = os.path.dirname(self.state.document.current_source)
        if 'request' in self.options:
            request = utils.resolve_path(self.options['request'], cwd)
            with open(request) as fp:
                self.content = StringList(
                    list(map(str.rstrip, fp.readlines())), request)

        # Wrap and render main directive as 'http-example-http'
        klass = 'http-example-http'
        container = nodes.container('', classes=[klass])
        container.append(nodes.caption('', 'http'))
        container.extend(super(HTTPExample, self).run())

        # Init result node list
        result = [container]

        # Append builder responses
        for name in chosen_builders:
            raw = ('\r\n'.join(self.content)).encode('utf-8')
            request = parsers.parse_request(raw, config.httpexample_scheme)
            builder_, language = AVAILABLE_BUILDERS[name]
            command = builder_(request)

            content = StringList([command], self.content.source(0))
            options = self.options.copy()
            options.pop('name', None)
            options.pop('caption', None)

            block = CodeBlock('code-block', [language], options, content,
                              self.lineno, self.content_offset,
                              self.block_text, self.state, self.state_machine)

            # Wrap and render main directive as 'http-example-{name}'
            klass = 'http-example-{}'.format(name)
            container = nodes.container('', classes=[klass])
            container.append(nodes.caption('', name))
            container.extend(block.run())

            # Append to result nodes
            result.append(container)

        # Append optional response
        if 'response' in self.options:
            response = utils.resolve_path(self.options['response'], cwd)
            with open(response) as fp:
                content = StringList(list(map(str.rstrip, fp.readlines())),
                                     response)

            options = self.options.copy()
            options.pop('name', None)
            options.pop('caption', None)

            block = CodeBlock('code-block', ['http'], options, content,
                              self.lineno, self.content_offset,
                              self.block_text, self.state, self.state_machine)

            # Wrap and render main directive as 'http-example-response'
            klass = 'http-example-response'
            container = nodes.container('', classes=[klass])
            container.append(nodes.caption('', 'response'))
            container.extend(block.run())

            # Append to result nodes
            result.append(container)

        # Final wrap
        container_node = nodes.container('', classes=['http-example'])
        container_node.extend(result)

        return [container_node]
def test_merge_dicts():
    assert utils.merge_dicts({'a': 'b'}, {'c': 'd'}) == {'a': 'b', 'c': 'd'}
    assert utils.merge_dicts({'a': 'b'}, {'a': 'c'}) == {'a': 'c'}
class HTTPExample(CodeBlock):

    required_arguments = 0
    optional_arguments = len(AVAILABLE_BUILDERS)

    option_spec = utils.merge_dicts(CodeBlock.option_spec, {
        'request': directives.unchanged,
        'response': directives.unchanged,
    })

    def run(self):
        config = self.state.document.settings.env.config

        # Read enabled builders; Defaults to None
        if self.arguments:
            chosen_builders = choose_builders(self.arguments)
        else:
            chosen_builders = []

        # Enable 'http' language for http part
        self.arguments = ['http']

        # split the request and optional response in the content.
        # The separator is two empty lines followed by a line starting with
        # 'HTTP/'
        request_content = StringList()
        response_content = None
        emptylines_count = 0
        in_response = False
        for i, line in enumerate(self.content):
            source = self.content.source(i)
            if in_response:
                response_content.append(line, source)
            else:
                if emptylines_count >= 2 and line.startswith('HTTP/'):
                    in_response = True
                    response_content = StringList()
                    response_content.append(line, source)
                elif line == '':
                    emptylines_count += 1
                else:
                    request_content.extend(
                        StringList([''] * emptylines_count, source))
                    emptylines_count = 0
                    request_content.append(line, source)

        # Load optional external request
        cwd = os.path.dirname(self.state.document.current_source)
        if 'request' in self.options:
            request = utils.resolve_path(self.options['request'], cwd)
            with open(request) as fp:
                request_content = StringList(
                    list(map(str.rstrip, fp.readlines())), request)

        # Load optional external response
        if 'response' in self.options:
            response = utils.resolve_path(self.options['response'], cwd)
            with open(response) as fp:
                response_content = StringList(
                    list(map(str.rstrip, fp.readlines())), response)

        # reset the content to just the request
        self.content = request_content

        # Wrap and render main directive as 'http-example-http'
        klass = 'http-example-http'
        container = nodes.container('', classes=[klass])
        container.append(nodes.caption('', 'http'))
        container.extend(super(HTTPExample, self).run())

        # Init result node list
        result = [container]

        # Append builder responses
        for name in chosen_builders:
            raw = ('\r\n'.join(request_content)).encode('utf-8')
            request = parsers.parse_request(raw, config.httpexample_scheme)
            builder_, language = AVAILABLE_BUILDERS[name]
            command = builder_(request)

            content = StringList([command], request_content.source(0))
            options = self.options.copy()
            options.pop('name', None)
            options.pop('caption', None)

            block = CodeBlock(
                'code-block',
                [language],
                options,
                content,
                self.lineno,
                self.content_offset,
                self.block_text,
                self.state,
                self.state_machine
            )

            # Wrap and render main directive as 'http-example-{name}'
            klass = 'http-example-{}'.format(name)
            container = nodes.container('', classes=[klass])
            container.append(nodes.caption('', name))
            container.extend(block.run())

            # Append to result nodes
            result.append(container)

        # Append optional response
        if response_content:
            options = self.options.copy()
            options.pop('name', None)
            options.pop('caption', None)

            block = CodeBlock(
                'code-block',
                ['http'],
                options,
                response_content,
                self.lineno,
                self.content_offset,
                self.block_text,
                self.state,
                self.state_machine
            )

            # Wrap and render main directive as 'http-example-response'
            klass = 'http-example-response'
            container = nodes.container('', classes=[klass])
            container.append(nodes.caption('', 'response'))
            container.extend(block.run())

            # Append to result nodes
            result.append(container)

        # Final wrap
        container_node = nodes.container('', classes=['http-example'])
        container_node.extend(result)

        return [container_node]