From f30c5eb9e9a9f118380c32fc357d666140ce9b21 Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Fri, 8 Dec 2017 20:10:18 +0530 Subject: [PATCH 01/13] Add utility to find the templates dir path --- firefly/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firefly/utils.py b/firefly/utils.py index fef6be9..8627b09 100644 --- a/firefly/utils.py +++ b/firefly/utils.py @@ -13,6 +13,11 @@ def json_encode(data): def is_file(obj): return hasattr(obj, "read") +def get_template_path(): + firefly_module = __import__('firefly') + file = firefly_module.__file__ + return '/'.join(file.split('/')[:-1]) + '/templates' + class FileIter: def __init__(self, fileobj, chunk_size=4096): self.fileobj = fileobj From 276930237e69c28c7a3f1d9f15e1726837e69a4f Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Fri, 8 Dec 2017 20:10:44 +0530 Subject: [PATCH 02/13] Adds functionality to render a showcase page --- firefly/app.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/firefly/app.py b/firefly/app.py index a284ac0..9871789 100644 --- a/firefly/app.py +++ b/firefly/app.py @@ -1,10 +1,11 @@ import cgi from webob import Request, Response from webob.exc import HTTPNotFound +from jinja2 import FileSystemLoader, Environment import json import logging from .validator import validate_args, ValidationError -from .utils import json_encode, is_file, FileIter +from .utils import json_encode, is_file, FileIter, get_template_path from .version import __version__ import threading @@ -47,6 +48,24 @@ def generate_index(self): } return help_dict + def generate_showcase(self): + # loader = FileSystemLoader(searchpath='./templates') + loader = FileSystemLoader(searchpath=get_template_path()) + env = Environment(loader=loader) + template = env.get_template('index.html') + functions = [ + {'name': name, 'path': spec['path'], 'doc': spec['doc'], 'parameters': spec['parameters']} + for name, spec in self.generate_function_list().items() + ] + html = template.render({ + 'name': 'stub', # TODO Adds support for naming the API + 'functions': functions + }) + response = Response(content_type='text/html') + response.status = 200 + response.text = html + return response + def __call__(self, environ, start_response): request = Request(environ) response = self.process_request(request) @@ -73,7 +92,9 @@ def process_request(self, request): ctx.request = request path = request.path_info - if path in self.mapping: + if path == "/index.html": + return self.generate_showcase() + elif path in self.mapping: func = self.mapping[path] response = func(request) else: From fe2cd9a050f9d829dcf2db52402ba5e26de68ecd Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Fri, 8 Dec 2017 20:23:50 +0530 Subject: [PATCH 03/13] Updates requirement.txt with Jinja2 --- requirements.txt | 1 + setup.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 15d89b2..87205b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ WebOb==1.7.2 requests>=2.18.1 PyYAML==3.12 funcsigs==1.0.2 ; python_version < '3' +Jinja2==2.10 diff --git a/setup.py b/setup.py index 1b81eb1..87143a4 100644 --- a/setup.py +++ b/setup.py @@ -77,7 +77,8 @@ def get_version(): 'gunicorn==19.7.1', 'WebOb==1.7.2', 'requests==2.18.1', - 'PyYAML==3.12' + 'PyYAML==3.12', + 'Jinja2==2.10' ] if PY2: From 07c2bd3bc290990e17fba490bcdd4159242a96af Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 10:31:51 +0530 Subject: [PATCH 04/13] Moves the FileSystemLoader so that it is instantiated onle once. --- firefly/app.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firefly/app.py b/firefly/app.py index 9871789..dd6e899 100644 --- a/firefly/app.py +++ b/firefly/app.py @@ -23,6 +23,10 @@ ctx = threading.local() ctx.request = None +loader = FileSystemLoader(searchpath=get_template_path()) +env = Environment(loader=loader) +template = env.get_template('index.html') + class Firefly(object): def __init__(self, auth_token=None): self.mapping = {} @@ -49,10 +53,6 @@ def generate_index(self): return help_dict def generate_showcase(self): - # loader = FileSystemLoader(searchpath='./templates') - loader = FileSystemLoader(searchpath=get_template_path()) - env = Environment(loader=loader) - template = env.get_template('index.html') functions = [ {'name': name, 'path': spec['path'], 'doc': spec['doc'], 'parameters': spec['parameters']} for name, spec in self.generate_function_list().items() From c3c929eef49b40ee1f63f3d4343ecb85f30c3658 Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 10:40:02 +0530 Subject: [PATCH 05/13] Moves the browser path to /docs --- firefly/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firefly/app.py b/firefly/app.py index dd6e899..9918e09 100644 --- a/firefly/app.py +++ b/firefly/app.py @@ -92,7 +92,7 @@ def process_request(self, request): ctx.request = request path = request.path_info - if path == "/index.html": + if path == "/docs": return self.generate_showcase() elif path in self.mapping: func = self.mapping[path] From d4bf9d61ec6927aae77b2411cc9e5d7784294468 Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 10:44:15 +0530 Subject: [PATCH 06/13] Adds the browser UI template --- firefly/templates/index.html | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 firefly/templates/index.html diff --git a/firefly/templates/index.html b/firefly/templates/index.html new file mode 100644 index 0000000..12d4fcf --- /dev/null +++ b/firefly/templates/index.html @@ -0,0 +1,68 @@ + + + + + + + {% if name %}{{ name }} | {% else %}{% endif %}firefly | API Browser + + + + } +
+

{% if name %}{{ name }} | {% else %}{% endif %}firefly | API Browser

+

Version: {{ version }}

+
+ {% for function in functions %} +
+
+
+

+
{{ function.path }}
+ {{ function.name }} +

+
+
+
+

Doc

+

+ {% if function.doc %} + {{ function.doc }} + {% else %} + No docstring provided + {% endif %} +

+

Parameters

+ + + + + + + + + + {% for param in function.parameters %} + + + + + + {% endfor %} + +
#NameKind
{{ loop.index }}{{ param.name }}{{ param.kind }}
+
+
+
+
+ {% endfor %} +
+
+ + + + From 817e3a674740e55787138439df85e150092e834b Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 10:45:25 +0530 Subject: [PATCH 07/13] Removes the instance of API name --- firefly/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/firefly/app.py b/firefly/app.py index 9918e09..fcc000c 100644 --- a/firefly/app.py +++ b/firefly/app.py @@ -58,7 +58,6 @@ def generate_showcase(self): for name, spec in self.generate_function_list().items() ] html = template.render({ - 'name': 'stub', # TODO Adds support for naming the API 'functions': functions }) response = Response(content_type='text/html') From 35eee6e9a574094f41866c67203b70f51b0e01f6 Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 13:14:42 +0530 Subject: [PATCH 08/13] Updates the index template with usage instructions --- firefly/templates/index.html | 104 +++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/firefly/templates/index.html b/firefly/templates/index.html index 12d4fcf..29211bb 100644 --- a/firefly/templates/index.html +++ b/firefly/templates/index.html @@ -15,52 +15,74 @@ }

{% if name %}{{ name }} | {% else %}{% endif %}firefly | API Browser

+ {% if version %}

Version: {{ version }}

-
- {% for function in functions %} -
-
-
-

-
{{ function.path }}
- {{ function.name }} -

-
-
-
-

Doc

-

- {% if function.doc %} - {{ function.doc }} - {% else %} - No docstring provided - {% endif %} -

-

Parameters

- - - - - - - - - - {% for param in function.parameters %} - - - - - - {% endfor %} - -
#NameKind
{{ loop.index }}{{ param.name }}{{ param.kind }}
+ {% endif %} +
+
+
+ {% for function in functions %} +
+
+
+

+
{{ function.path }}
+ {{ function.name }} +

+
+
+
+

Doc

+

+ {% if function.doc %} + {{ function.doc }} + {% else %} + No docstring provided + {% endif %} +

+

Parameters

+ + + + + + + + + + {% for param in function.parameters %} + + + + + + {% endfor %} + +
#NameKind
{{ loop.index }}{{ param.name }}{{ param.kind }}
+
+
+ {% endfor %} +
+
+
+

The API can be used by using firefly-client

+

Install it by:

+
+ +$ pip install firefly +
+
+ +>>> import firefly
+>>> client = firefly.Client("<api_ip>")
+>>> client.<func_name>(<parameters>)
+<function_response> +
+
- {% endfor %} -
From 456b0f4cc7b42ee8a3f108b009146b5485c48b1d Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 17:04:17 +0530 Subject: [PATCH 09/13] Adds support to show the host url in docs --- firefly/app.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/firefly/app.py b/firefly/app.py index fcc000c..325abef 100644 --- a/firefly/app.py +++ b/firefly/app.py @@ -52,12 +52,13 @@ def generate_index(self): } return help_dict - def generate_showcase(self): + def generate_showcase(self, **kwargs): functions = [ {'name': name, 'path': spec['path'], 'doc': spec['doc'], 'parameters': spec['parameters']} for name, spec in self.generate_function_list().items() ] html = template.render({ + 'host_url': kwargs['host_url'], 'functions': functions }) response = Response(content_type='text/html') @@ -92,7 +93,7 @@ def process_request(self, request): path = request.path_info if path == "/docs": - return self.generate_showcase() + return self.generate_showcase(host_url=request.environ['HTTP_HOST']) elif path in self.mapping: func = self.mapping[path] response = func(request) From 8844d3421f0793fd92a9d395a0b20a0128ae195a Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 17:04:37 +0530 Subject: [PATCH 10/13] Updates docs template with reviews --- firefly/templates/index.html | 52 ++++++++++++++---------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/firefly/templates/index.html b/firefly/templates/index.html index 29211bb..fe4f703 100644 --- a/firefly/templates/index.html +++ b/firefly/templates/index.html @@ -12,14 +12,15 @@ } - } +
+

{% if name %}{{ name }} | {% else %}{% endif %}firefly | API Browser

{% if version %}

Version: {{ version }}

{% endif %}
-
+
{% for function in functions %}
@@ -32,7 +33,6 @@

-

Doc

{% if function.doc %} {{ function.doc }} @@ -41,24 +41,11 @@

Doc

{% endif %}

Parameters

- - - - - - - - - - {% for param in function.parameters %} - - - - - - {% endfor %} - -
#NameKind
{{ loop.index }}{{ param.name }}{{ param.kind }}
+
    + {% for param in function.parameters %} +
  • {{ param.name }}
  • + {% endfor %} +
@@ -66,21 +53,22 @@

Parameters

{% endfor %}
-
+

The API can be used by using firefly-client

-

Install it by:

+

Install it using:

- -$ pip install firefly - + + $ pip install firefly +
+

Usage:

- ->>> import firefly
->>> client = firefly.Client("<api_ip>")
->>> client.<func_name>(<parameters>)
-<function_response> -
+ + >>> import firefly
+ >>> client = firefly.Client('{{ host_url }}')
+ >>> client.function_name(<parameters>)
+ <function_response> +
From 6aacc8d3e43de14ad666350285e25518bec9ef59 Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 18:36:31 +0530 Subject: [PATCH 11/13] Uses jinja2.PackageLoader instead of FileSystemLoader along with computing the template path manually --- firefly/app.py | 5 ++--- firefly/utils.py | 5 ----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/firefly/app.py b/firefly/app.py index 325abef..a23ac50 100644 --- a/firefly/app.py +++ b/firefly/app.py @@ -1,7 +1,7 @@ import cgi from webob import Request, Response from webob.exc import HTTPNotFound -from jinja2 import FileSystemLoader, Environment +from jinja2 import PackageLoader, Environment import json import logging from .validator import validate_args, ValidationError @@ -23,8 +23,7 @@ ctx = threading.local() ctx.request = None -loader = FileSystemLoader(searchpath=get_template_path()) -env = Environment(loader=loader) +env = Environment(loader=PackageLoader('firefly', 'templates')) template = env.get_template('index.html') class Firefly(object): diff --git a/firefly/utils.py b/firefly/utils.py index 8627b09..fef6be9 100644 --- a/firefly/utils.py +++ b/firefly/utils.py @@ -13,11 +13,6 @@ def json_encode(data): def is_file(obj): return hasattr(obj, "read") -def get_template_path(): - firefly_module = __import__('firefly') - file = firefly_module.__file__ - return '/'.join(file.split('/')[:-1]) + '/templates' - class FileIter: def __init__(self, fileobj, chunk_size=4096): self.fileobj = fileobj From fa5dfcb30d57592fb3a57cf713ff6ca1ca63d969 Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 18:37:24 +0530 Subject: [PATCH 12/13] Refactors generate_showcase to render_docs --- firefly/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firefly/app.py b/firefly/app.py index a23ac50..6432cca 100644 --- a/firefly/app.py +++ b/firefly/app.py @@ -51,7 +51,7 @@ def generate_index(self): } return help_dict - def generate_showcase(self, **kwargs): + def render_docs(self, **kwargs): functions = [ {'name': name, 'path': spec['path'], 'doc': spec['doc'], 'parameters': spec['parameters']} for name, spec in self.generate_function_list().items() @@ -92,7 +92,7 @@ def process_request(self, request): path = request.path_info if path == "/docs": - return self.generate_showcase(host_url=request.environ['HTTP_HOST']) + return self.render_docs(host_url=request.environ['HTTP_HOST']) elif path in self.mapping: func = self.mapping[path] response = func(request) From dfeab61fba41fe451c12e14f14a5058113ab1391 Mon Sep 17 00:00:00 2001 From: Nabarun Pal Date: Mon, 11 Dec 2017 19:22:09 +0530 Subject: [PATCH 13/13] Removes non-existing import --- firefly/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firefly/app.py b/firefly/app.py index 6432cca..c9920be 100644 --- a/firefly/app.py +++ b/firefly/app.py @@ -5,7 +5,7 @@ import json import logging from .validator import validate_args, ValidationError -from .utils import json_encode, is_file, FileIter, get_template_path +from .utils import json_encode, is_file, FileIter from .version import __version__ import threading