Skip to content

Commit

Permalink
Add more Faker examples (#40)
Browse files Browse the repository at this point in the history
* Add more Faker examples

* Add a test case to demonstrate the issues

* Turn 'fake' helper into a context object in the handlebars templating

* Add escapeHtml helper

* Add tojson helper

* Add array helper

* Add more Faker examples to the documentation

* Add replace helper
  • Loading branch information
M. Mert Yıldıran authored Jan 18, 2021
1 parent 380e2c2 commit c81382e
Show file tree
Hide file tree
Showing 20 changed files with 415 additions and 52 deletions.
94 changes: 91 additions & 3 deletions Templating.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,72 @@ Here is a list of date shifting examples as a Handlebars response template:
### Faker

[Faker](https://faker.readthedocs.io/en/master/providers.html) library is provided for generating some dynamic data.
It is available as `fake` object. Refer to the [official docs](https://faker.readthedocs.io/en/master/providers.html) for all capabilities. Below are some examples:
It is available as `fake` object. Refer to the [official docs](https://faker.readthedocs.io/en/master/providers.html)
for all capabilities. Below are some examples (Handlebars):

- `fake.first_name`
- `fake.last_name`
```hbs
{
"hexify_args": "{{ fake.hexify text="MAC Address: ^^:^^:^^:^^:^^:^^" upper=true }}",
"lexify_args": "{{ fake.lexify text="Random Identifier: ??????????" }}",
"numerify_args": "{{ fake.numerify text="Intel Core i%-%%##K vs AMD Ryzen % %%##X" }}",
"random_choices": "{{ fake.random_choices elements=( array 'a' 'b' 'c' 'd' 'e' ) }}",
"random_digit": {{ fake.random_digit }},
"random_element": "{{ fake.random_element elements=( array 'a' 'b' 'c' 'd' 'e' ) }}",
"random_elements": {{ tojson ( fake.random_elements elements=( array 'a' 'b' 'c' 'd' 'e' ) length=3 unique=True ) }},
"random_int_args": {{ fake.random_int min=10000 max=50000 step=500 }},
"random_letter": "{{ fake.random_letter }}",
"random_letters": {{ tojson ( fake.random_letters ) }},
"random_letters_args": {{ tojson ( fake.random_letters length=32 ) }},
"random_lowercase_letter": "{{ fake.random_lowercase_letter }}",
"random_sample": {{ tojson ( fake.random_sample elements=( array 'a' 'b' 'c' 'd' 'e' ) ) }},
"random_uppercase_letter": "{{ fake.random_uppercase_letter }}",
"first_name": "{{ fake.first_name }}",
"first_name_female": "{{ fake.first_name_female }}",
"first_name_male": "{{ fake.first_name_male }}",
"first_name_nonbinary": "{{ fake.first_name_nonbinary }}",
"last_name": "{{ fake.last_name }}",
"last_name_female": "{{ fake.last_name_female }}",
"last_name_male": "{{ fake.last_name_male }}",
"last_name_nonbinary": "{{ fake.last_name_nonbinary }}",
"address": "{{ replace ( fake.address ) old='\n' new='\\n' }}",
"city": "{{ fake.city }}",
"country": "{{ fake.country }}"
}
```

Rendered:

```json
{
"hexify_args": "MAC Address: 84:DE:AD:1C:AD:D7",
"lexify_args": "Random Identifier: ZDGINMgIkX",
"numerify_args": "Intel Core i7-8517K vs AMD Ryzen 3 8887X",
"random_choices": "['b', 'a', 'd', 'a', 'b']",
"random_digit": 9,
"random_element": "d",
"random_elements": ["a", "a", "d"],
"random_int_args": 26500,
"random_letter": "A",
"random_letters": ["M", "S", "N", "T", "r", "m", "p", "j", "R", "n", "g", "g", "A", "w", "o", "d"],
"random_letters_args": ["F", "L", "X", "R", "Z", "T", "f", "k", "C", "v", "U", "d", "d", "S", "p", "j", "s", "F", "M", "X", "k", "J", "P", "R", "W", "m", "i", "A", "x", "o", "r", "H"],
"random_lowercase_letter": "i",
"random_sample": ["c", "a", "b"],
"random_uppercase_letter": "U",
"first_name": "Mark",
"first_name_female": "Sharon",
"first_name_male": "Brian",
"first_name_nonbinary": "Jessica",
"last_name": "Campbell",
"last_name_female": "Rodriguez",
"last_name_male": "Combs",
"last_name_nonbinary": "Mcmillan",
"address": "035 Angela Brook\nElizabethhaven, MO 35984",
"city": "Hannaport",
"country": "Burkina Faso"
}
```

An extended list of Faker examples can be found in: [Handlebars](https://github.com/up9inc/mockintosh/blob/main/tests/configs/json/hbs/core/templates/faker.json.hbs) and [Jinja2](https://github.com/up9inc/mockintosh/blob/main/tests/configs/json/j2/core/templates/faker.json.j2)

### Counters

Expand Down Expand Up @@ -138,3 +200,29 @@ And some less frequently used:
### Using JSONPath Syntax

You can reference certain fields from request's JSON by using `jsonPath` helper like this: `jsonPath request.json '$.key'`.

### Other Template Helpers

#### `escapeHtml(text)`

`{{ escapeHtml '& < \" >' }}` a helper to escape HTML special characters. (see [`html.escape`](https://wiki.python.org/moin/EscapingHtml))

#### `tojson(text)` (Handlebars)

The equivalent of [`tojson`](https://jinja.palletsprojects.com/en/2.11.x/templates/#tojson) filter in Jinja2.

Jinja2 usage: `{{ fake.random_letters() | tojson }}`

Handlebars usage: `{{ tojson ( fake.random_letters ) }}`

#### `array(*args)` (Handlebars)

Provides array support in parameters. `{{ array 'a' 'b' 'c' 'd' 'e' }}` returns `['a', 'b', 'c', 'd', 'e']`.

#### `replace(text, old, new, count=None)` (Handlebars)

The equivalent of [`replace`](https://jinja.palletsprojects.com/en/2.11.x/templates/#replace) filter in Jinja2.

Jinja2 usage: `{{ fake.address() | replace('\n','\\n') }}`

Handlebars usage: `{{ replace ( fake.address ) old='\n' new='\\n' }}`
7 changes: 4 additions & 3 deletions mockintosh/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,11 +596,11 @@ def loop_alternative(self, alternative, key, subkey):

def common_template_renderer(self, template_engine, text):
if template_engine == PYBARS:
from mockintosh.hbs.methods import fake, counter, json_path
from mockintosh.hbs.methods import fake, counter, json_path, escape_html
self.custom_context['random'] = hbs_random
self.custom_context['date'] = hbs_date
elif template_engine == JINJA:
from mockintosh.j2.methods import fake, counter, json_path
from mockintosh.j2.methods import fake, counter, json_path, escape_html
self.custom_context['random'] = j2_random
self.custom_context['date'] = j2_date
else:
Expand All @@ -613,7 +613,8 @@ def common_template_renderer(self, template_engine, text):
inject_methods=[
fake,
counter,
json_path
json_path,
escape_html
],
add_params_callback=self.add_params,
fill_undefineds=True
Expand Down
43 changes: 41 additions & 2 deletions mockintosh/hbs/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@
import os
import binascii
import time
import html
import json
from datetime import datetime
from datetime import timedelta
from uuid import uuid4

from jsonpath_ng import parse as jsonpath_parse
from pybars import PybarsError
from faker import Faker

from mockintosh.methods import _handlebars_add_to_context


def fake(this, fake, attr):
return getattr(fake, attr)()
def fake():
# Fake fake :)
pass


def reg_ex(this, regex, *args, **kwargs):
Expand Down Expand Up @@ -71,6 +75,29 @@ def counter(this, name):
return number


def escape_html(this, text):
return html.escape(text)


def tojson(this, text):
return json.dumps(text) \
.replace(u'<', u'\\u003c') \
.replace(u'>', u'\\u003e') \
.replace(u'&', u'\\u0026') \
.replace(u"'", u'\\u0027')


def array(this, *args):
return [*args]


def replace(this, text, old, new, count=None):
if count is None:
count = -1
text = text.replace(str(old), str(new))
return text


class Random():

def __init__(self):
Expand Down Expand Up @@ -121,3 +148,15 @@ def date(
else:
now = now + shift_time
return now.strftime(pattern)


class HbsFaker(Faker):
def __getattr__(self, name):
attr = Faker.__getattr__(self, name)
if hasattr(attr, '__call__'):
def newfunc(this, *args, **kwargs):
result = attr(*args, **kwargs)
return result
return newfunc
else:
return attr
5 changes: 5 additions & 0 deletions mockintosh/j2/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import os
import binascii
import time
import html
from datetime import datetime
from datetime import timedelta
from uuid import uuid4
Expand Down Expand Up @@ -75,6 +76,10 @@ def counter(context, name):
return number


def escape_html(text):
return html.escape(text)


class Random():

def __init__(self):
Expand Down
34 changes: 16 additions & 18 deletions mockintosh/templating.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
from mockintosh.constants import SUPPORTED_ENGINES, PYBARS, JINJA, JINJA_VARNAME_DICT, SPECIAL_CONTEXT
from mockintosh.exceptions import UnsupportedTemplateEngine
from mockintosh.methods import _to_camel_case
from mockintosh.hbs.methods import fake as hbs_fake
from mockintosh.hbs.methods import HbsFaker, tojson, array, replace

compiler = Compiler()
faker = Faker()
hbs_faker = HbsFaker()

debug_mode = environ.get('MOCKINTOSH_DEBUG', False)

Expand Down Expand Up @@ -63,10 +64,10 @@ def check_engine_support(self):

def render_handlebars(self):
context, helpers = self.add_globals(compiler._compiler, helpers={})
template = compiler.compile(self.text)
try:
template = compiler.compile(self.text)
compiled = template(context, helpers=helpers)
except (PybarsError, TypeError) as e:
except (PybarsError, TypeError, SyntaxError) as e:
if self.fill_undefineds:
if debug_mode:
raise e
Expand Down Expand Up @@ -112,11 +113,6 @@ def render_jinja(self):
return compiled, copy.deepcopy(env.globals[JINJA_VARNAME_DICT])

def add_globals(self, template, helpers=None):
fake = None
# Create the faker object if `fake` is in the `inject_methods`
if 'fake' in self.inject_methods_name_list:
fake = faker

# To provide the support of both PYBARS and JINJA
context = {}
engine = PYBARS
Expand All @@ -126,11 +122,22 @@ def add_globals(self, template, helpers=None):
helpers = template.globals
context = helpers

if engine == PYBARS:
self.inject_methods += [
tojson,
array,
replace
]

# Inject the methods:
for method in self.inject_methods:
if method.__name__ == 'fake':
logging.debug('Inject Faker object into the template.')
helpers[method.__name__] = fake
if engine == PYBARS:
# Workaround to provide Faker support in PYBARS
context['fake'] = hbs_faker
else:
context['fake'] = faker
else:
helpers[_to_camel_case(method.__name__)] = method

Expand All @@ -142,13 +149,4 @@ def add_globals(self, template, helpers=None):
if self.add_params_callback is not None:
context = self.add_params_callback(context)

# Workaround to provide Faker support in PYBARS
if engine == PYBARS and 'fake' in self.inject_methods_name_list:
logging.debug('Use Handlebars version of Faker.')

def super_fake(this, *args, **kwargs):
return hbs_fake(this, fake, *args, **kwargs)

helpers['fake'] = super_fake

return context, helpers
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ Faker==4.18.0
tornado==6.0.4
PyYAML==5.3.1
jsonschema==3.2.0
pybars4>=0.9.9
pybars4>=0.9.11
accept-types==0.4.1
jsonpath-ng==1.5.2
4 changes: 2 additions & 2 deletions tests/configs/json/hbs/common/templates/companies.json.hbs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"companies": [
{
"name": "{{ fake "company" }}",
"motto": "{{ fake "catch_phrase" }}"
"name": "{{ fake.company }}",
"motto": "{{ fake.catch_phrase }}"
}
],
"total": 2
Expand Down
4 changes: 2 additions & 2 deletions tests/configs/json/hbs/common/templates/user.json.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": {{ id }},
"firstName": "{{ fake "first_name" }}",
"lastName": "{{ fake "last_name" }}",
"firstName": "{{ fake.first_name }}",
"lastName": "{{ fake.last_name }}",
"friends": [
{
"id": "{{ random.uuid4 }}"
Expand Down
4 changes: 2 additions & 2 deletions tests/configs/json/hbs/common/templates/users.json.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"users": [
{
"id": {{ random.int 10000 100000 }},
"firstName": "{{ fake "first_name" }}",
"lastName": "{{ fake "last_name" }}",
"firstName": "{{ fake.first_name }}",
"lastName": "{{ fake.last_name }}",
"friends": [
{
"id": "{{ random.uuid4 }}"
Expand Down
21 changes: 21 additions & 0 deletions tests/configs/json/hbs/core/faker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"templatingEngine": "Handlebars",
"services": [
{
"name": "Mock for Service1",
"port": 8001,
"endpoints": [
{
"path": "/faker",
"method": "GET",
"response": {
"headers": {
"Content-Type": "application/json; charset=UTF-8"
},
"body": "@templates/faker.json.hbs"
}
}
]
}
]
}
Loading

0 comments on commit c81382e

Please sign in to comment.