예제 #1
0
    def release(self):
        """Release service resource."""

        self.storage.disconnect()
        Cause.reset()
        Config.reset()
        Logger.reset()
예제 #2
0
    def run(self):
        """Run Snippy API server."""

        options = {
            'bind': Config.server_host,
            'ca_certs': Config.server_ssl_ca_cert,
            'certfile': Config.server_ssl_cert,
            'ciphers': 'ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA',
            'keyfile': Config.server_ssl_key,
            'logger_class': CustomGunicornLogger,
            'on_exit': SnippyServer.on_exit,
            'post_worker_init': SnippyServer.post_worker_init,
            'pre_fork': SnippyServer.pre_fork,
            'pre_request': SnippyServer.pre_request,
            'ssl_version': ssl.PROTOCOL_TLSv1_2,
            'workers': 1
        }
        self._logger.debug('run rest api server application with base path: %s', Config.server_base_path_rest)
        try:
            self.api = falcon.API(media_type='application/vnd.api+json')
        except AttributeError:
            raise ImportError
        snippet = Snippet(self.storage, run_cli=False)
        solution = Solution(self.storage, run_cli=False)
        reference = Reference(self.storage, run_cli=False)
        groups = Fields(self.storage, Const.GROUPS, run_cli=False)
        tags = Fields(self.storage, Const.TAGS, run_cli=False)
        self.api.req_options.media_handlers.update({'application/vnd.api+json': falcon.media.JSONHandler()})
        self.api.resp_options.media_handlers.update({'application/vnd.api+json': falcon.media.JSONHandler()})
        self.api.add_route(Config.server_base_path_rest.rstrip('/'), ApiHello())
        self.api.add_route(urljoin(Config.server_base_path_rest, 'hello'), ApiHello())
        self.api.add_route(urljoin(Config.server_base_path_rest, 'snippets'), ApiSnippets(snippet))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'snippets/{identity}'), ApiSnippetsId(snippet))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'snippets/{identity}/{field}'), ApiSnippetsIdField(snippet))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'solutions'), ApiSolutions(solution))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'solutions/{identity}'), ApiSolutionsId(solution))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'solutions/{identity}/{field}'), ApiSolutionsIdField(solution))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'references'), ApiReferences(reference))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'references/{identity}'), ApiReferencesId(reference))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'references/{identity}/{field}'), ApiReferencesIdField(reference))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'groups'), ApiGroups(groups))
        self.api.add_route(urljoin(Config.server_base_path_rest, 'tags'), ApiTags(tags))

        # Reset cause just before starting the server. If there were any
        # failures during the server statup phase, they are still stored
        # in the Cause object when the server runs. If this is the case,
        # the first response sent by the server will be error response
        # even when the HTTP request was processed successfully.
        Cause.reset()

        # The signal handler manipulation and the flush below prevent the
        # 'broken pipe' error with grep. See more information from Logger
        # print_stdout method. Example command below works because of this.
        #
        # $ snippy --server-host localhost:8080 -vv | grep -A2 -B2 'operation: run :'
        signal_sigpipe = getsignal(SIGPIPE)
        signal(SIGPIPE, SIG_DFL)
        SnippyServer(self.api, options).run()
        sys.stdout.flush()
        signal(SIGPIPE, signal_sigpipe)
예제 #3
0
    def on_get(self, request, response):
        """Search unique resource attributes.

        Search is made from all content categories by default.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        if 'scat' not in request.params:
            request.params['scat'] = Const.CATEGORIES
        api = Api(self._category, Api.UNIQUE, request.params)
        Config.load(api)
        self._content.run()
        if not self._content.uniques:
            Cause.push(
                Cause.HTTP_NOT_FOUND,
                'cannot find unique fields for %s attribute' % self._category)
        if Cause.is_ok():
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.fields(self._category,
                                            self._content.uniques, request,
                                            response)
            response.status = Cause.http_status()
        else:
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.error(Cause.json_message())
            response.status = Cause.http_status()

        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #4
0
    def on_post(self, request, response, **kwargs):  # pylint: disable=unused-argument
        """Create new resource.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        collection = Collection()
        data = Validate.json_object(request)
        for resource in data:
            api = Api(self._category, Api.CREATE, resource)
            Config.load(api)
            self._content.run(collection)
        if Cause.is_ok():
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.collection(collection, request, response)
            response.status = Cause.http_status()
        else:
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.error(Cause.json_message())
            response.status = Cause.http_status()
        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #5
0
    def on_get(self, request, response, sall=None, stag=None, sgrp=None):
        """Search resources.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
            sall (str): Search all ``sall`` path parameter.
            stag (str): Search tags ``stag`` path parameter.
            sgrp (str): Search groups ``sgrp`` path parameter.
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        if sall:
            request.params['sall'] = sall
        if stag:
            request.params['stag'] = stag
        if sgrp:
            request.params['sgrp'] = sgrp
        api = Api(self._category, Api.SEARCH, request.params)
        Config.load(api)
        self._content.run()
        if not self._content.collection and Config.search_limit != 0:
            Cause.push(Cause.HTTP_NOT_FOUND, 'cannot find resources')
        if Cause.is_ok():
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.collection(self._content.collection, request, response, pagination=True)
            response.status = Cause.http_status()
        else:
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.error(Cause.json_message())
            response.status = Cause.http_status()

        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #6
0
    def on_put(self, request, response, identity):
        """Update resource based on the resource ID.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
            identity (str): Partial or full message digest or UUID.
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        collection = Validate.json_object(request, identity)
        for resource in collection:
            api = Api(self._category, Api.UPDATE, resource)
            Config.load(api)
            self._content.run()
        if Cause.is_ok():
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.resource(self._content.collection, request, response, identity)
            response.status = Cause.http_status()
        else:
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.error(Cause.json_message())
            response.status = Cause.http_status()

        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #7
0
    def on_get(self, request, response, identity, field):
        """Get defined content field based on resource ID.

        If the given uuid matches to multiple resources or no resources at
        all, an error is returned. This conflicts against the JSON API v1.0
        specifications. See the Snippy documentation for more information.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
            identity (str): Partial or full message digest or UUID.
            field (str): Resource attribute.
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        local_params = {'identity': identity, 'fields': field}
        api = Api(self._category, Api.SEARCH, local_params)
        Config.load(api)
        self._content.run()
        if len(self._content.collection) != 1:
            Cause.push(Cause.HTTP_NOT_FOUND, 'content identity: %s was not unique and matched to: %d resources' %
                       (identity, len(self._content.collection)))
        if Cause.is_ok():
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.resource(self._content.collection, request, response, identity, field=field, pagination=False)
            response.status = Cause.http_status()
        else:
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.error(Cause.json_message())
            response.status = Cause.http_status()

        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #8
0
    def on_patch(self, request, response, identity):
        """Update partial resource based on resource ID.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
            identity (str): Partial or full message digest or UUID.
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        self.on_put(request, response, identity)
        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #9
0
    def on_delete(self, request, response, **kwargs):  # pylint: disable=unused-argument
        """Delete resource.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        Cause.push(Cause.HTTP_NOT_FOUND, 'cannot delete content without identified resource')
        response.content_type = ApiResource.MEDIA_JSON_API
        response.body = Generate.error(Cause.json_message())
        response.status = Cause.http_status()

        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #10
0
    def on_get(self, request, response, scat=None, sall=None, stag=None, sgrp=None):
        """Search unique groups.

        By default the search is made from all content categories.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
            scat (str): Search categories ``scat`` path parameter.
            sall (str): Search all ``sall`` path parameter.
            stag (str): Search tags ``stag`` path parameter.
            sgrp (str): Search groups ``sgrp`` path parameter.
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        if scat:
            request.params['scat'] = scat
        else:
            request.params['scat'] = Const.CATEGORIES

        if sall:
            request.params['sall'] = sall
        if stag:
            request.params['stag'] = stag
        if sgrp:
            request.params['sgrp'] = sgrp
        api = Api(self._category, Api.UNIQUE, request.params)
        Config.load(api)
        self._content.run()
        if not self._content.uniques:
            Cause.push(Cause.HTTP_NOT_FOUND, 'cannot find unique fields for groups attribute')
        if Cause.is_ok():
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.fields('groups', self._content.uniques, request, response)
            response.status = Cause.http_status()
        else:
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.error(Cause.json_message())
            response.status = Cause.http_status()

        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #11
0
    def run(self, args=None):
        """Run service.

        Args:
            list: Command line arguments.
        """

        if args:
            Config.load(Cli(args))

        if Config.failure:
            Cause.print_failure()

            return Cause.reset()

        if Config.run_server:
            self._run_server()
        elif Config.run_healthcheck:
            self._run_healthcheck()
        else:
            self._run_cli()

        return Cause.reset()
예제 #12
0
    def on_delete(self, request, response, identity):
        """Delete resource based on the resource ID.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
            identity (str): Partial or full message digest or UUID.
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        local_params = {'identity': identity}
        api = Api(self._category, Api.DELETE, local_params)
        Config.load(api)
        self._content.run()
        if Cause.is_ok():
            response.status = Cause.http_status()
        else:
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.error(Cause.json_message())
            response.status = Cause.http_status()

        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)
예제 #13
0
    def on_post(self, request, response, identity):
        """Update resource.

        Args:
            request (obj): Falcon Request().
            response (obj): Falcon Response().
            identity (str): Partial or full message digest or UUID.
        """

        self._logger.debug('run: %s %s', request.method, request.uri)
        if request.get_header('x-http-method-override', default='post').lower() == 'put':
            self.on_put(request, response, identity)
        elif request.get_header('x-http-method-override', default='post').lower() == 'patch':
            self.on_patch(request, response, identity)
        elif request.get_header('x-http-method-override', default='post').lower() == 'delete':
            self.on_delete(request, response, identity)
        else:
            Cause.push(Cause.HTTP_BAD_REQUEST, 'cannot create resource with id, use x-http-method-override to override the request')
            response.content_type = ApiResource.MEDIA_JSON_API
            response.body = Generate.error(Cause.json_message())
            response.status = Cause.http_status()

        Cause.reset()
        self._logger.debug('end: %s %s', request.method, request.uri)