Skip to content

Commit

Permalink
Rename 'background' parameter to match the URL value
Browse files Browse the repository at this point in the history
  • Loading branch information
jacebrowning committed May 8, 2021
1 parent cac3553 commit e2ffc1f
Show file tree
Hide file tree
Showing 12 changed files with 383 additions and 311 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# Revision History

## 9.0

- Renamed `image_url` body parameter to `background` to match the URL value.

## 8.5

- Added support for custom overlays when `style` is an image URL.
Expand Down
6 changes: 3 additions & 3 deletions app/models/template.py
Expand Up @@ -77,7 +77,7 @@ def image(self) -> Path:
def get_image(self, style: str = "") -> Path:
style = style or settings.DEFAULT_STYLE

if "://" in style:
if utils.urls.schema(style):
url = style
style = utils.text.fingerprint(url)

Expand Down Expand Up @@ -216,11 +216,11 @@ async def create(cls, url: str, *, force=False) -> "Template":
return template

async def check(self, style: str, *, force=False) -> bool:
if not style:
if style in {"", None, settings.DEFAULT_STYLE}:
return True
if style in self.styles:
return True
if "://" not in style:
if not utils.urls.schema(style):
logger.error(f"Invalid style for {self.id} template: {style}")
return False

Expand Down
4 changes: 4 additions & 0 deletions app/tests/test_utils_images.py
Expand Up @@ -32,6 +32,7 @@ def template():
# Formats


@pytest.mark.slow
@pytest.mark.parametrize(("id", "lines"), settings.TEST_IMAGES)
def test_png_images(images, id, lines):
template = models.Template.objects.get(id)
Expand Down Expand Up @@ -110,12 +111,14 @@ def test_unknown_template(images):
# Styles


@pytest.mark.slow
def test_alternate_style(images):
template = models.Template.objects.get("ds")
lines = ["one", "two", "three"]
utils.images.save(template, lines, style="maga", directory=images)


@pytest.mark.slow
@pytest.mark.asyncio
async def test_custom_style(images):
url = "https://sn56.scholastic.com/content/dam/classroom-magazines/sn56/issues/2019-20/031620/coronavirus/16-SN56-20200316-VirusOutbreak-PO-2.png"
Expand Down Expand Up @@ -146,6 +149,7 @@ def test_long_first_word(images):
utils.images.save(template, lines, directory=images)


@pytest.mark.slow
def test_text_wrap_when_font_is_too_small(images):
template = models.Template.objects.get("ds")
lines = ["this button seems to be ok to push"]
Expand Down
59 changes: 42 additions & 17 deletions app/tests/test_views_memes.py
Expand Up @@ -113,6 +113,11 @@ def it_returns_blank_templates_when_no_slug(expect, client, path, content_type):
expect(response.status) == 200
expect(response.headers["content-type"]) == content_type

def it_handles_placeholder_templates(expect, client):
request, response = client.get("/images/string/test.png")
expect(response.status) == 200
expect(response.headers["content-type"]) == "image/png"

def it_handles_unknown_templates(expect, client, unknown_template):
request, response = client.get(f"/images/{unknown_template.id}/test.png")
expect(response.status) == 404
Expand Down Expand Up @@ -195,42 +200,62 @@ def it_is_disabled_automatically_for_small_images(expect, client):
expect(len(response.content)) == len(small_content)

def describe_styles():
@pytest.fixture(
params=[
"/images/ds/one/two.png?",
"/images/custom/test.png?background=https://www.gstatic.com/webp/gallery/3.jpg&",
]
)
def base_url(request):
return request.param

@pytest.mark.slow
def it_supports_alternate_styles(expect, client):
request, response = client.get("/images/ds/one/two.png?style=maga")
expect(response.status) == 200
expect(response.headers["content-type"]) == "image/png"

def it_rejects_invalid_styles(expect, client):
request, response = client.get("/images/ds/one/two.png?style=foobar")
@pytest.mark.slow
def it_rejects_invalid_styles(expect, client, base_url):
request, response = client.get(base_url + "style=foobar")
expect(response.status) == 422
expect(response.headers["content-type"]) == "image/png"

def it_ignores_placeholder_values(expect, client):
request, response = client.get("/images/string/string.png?style=string")
@pytest.mark.slow
def it_ignores_placeholder_values(expect, client, base_url):
request, response = client.get(base_url + "style=string")
expect(response.status) == 200
expect(response.headers["content-type"]) == "image/png"

def describe_overlay():
def it_supports_custom_styles(expect, client):
@pytest.fixture(
params=[
"/images/fine/test.png?",
"/images/custom/test.png?background=https://www.gstatic.com/webp/gallery/3.jpg&",
]
)
def base_url(request):
return request.param

def it_supports_custom_styles(expect, client, base_url):
request, response = client.get(
"/images/fine/one/two.png"
"?style=https://www.gstatic.com/webp/gallery/4.jpg"
base_url + "style=https://www.gstatic.com/webp/gallery/4.jpg"
)
expect(response.status) == 200
expect(response.headers["content-type"]) == "image/png"

def it_requires_image_urls(expect, client):
request, response = client.get(
"/images/fine/test.png?style=http://example.com"
)
expect(response.status) == 422
@pytest.mark.slow
def it_requires_image_urls(expect, client, base_url):
request, response = client.get(base_url + "style=http://example.com")
expect(response.status) == 415
expect(response.headers["content-type"]) == "image/png"

def it_handles_missing_urls(expect, client):
@pytest.mark.slow
def it_handles_missing_urls(expect, client, base_url):
request, response = client.get(
"/images/fine/test.png?style=http://example.com/does_not_exist.png"
base_url + "style=http://example.com/does_not_exist.png"
)
expect(response.status) == 422
expect(response.status) == 415
expect(response.headers["content-type"]) == "image/png"

def describe_custom():
Expand Down Expand Up @@ -301,7 +326,7 @@ def describe_POST():
@pytest.mark.parametrize("as_json", [True, False])
def it_supports_custom_backgrounds(expect, client, as_json):
data = {
"image_url": "https://www.gstatic.com/webp/gallery/3.png",
"background": "https://www.gstatic.com/webp/gallery/3.png",
"text_lines[]": ["foo", "bar"],
"extension": "jpg",
}
Expand All @@ -316,7 +341,7 @@ def it_supports_custom_backgrounds(expect, client, as_json):

def it_redirects_if_requested(expect, client):
data = {
"image_url": "https://www.gstatic.com/webp/gallery/4.png",
"background": "https://www.gstatic.com/webp/gallery/4.png",
"text_lines": ["abc"],
"redirect": True,
}
Expand Down
2 changes: 1 addition & 1 deletion app/tests/test_views_templates.py
Expand Up @@ -58,7 +58,7 @@ def it_returns_an_image_url(expect, client, as_json):
@pytest.mark.parametrize("as_json", [True, False])
def it_supports_custom_backgrounds(expect, client, as_json):
data = {
"image_url": "https://www.gstatic.com/webp/gallery/3.png",
"background": "https://www.gstatic.com/webp/gallery/3.png",
"text_lines[]": ["foo", "bar"],
"extension": "jpg",
}
Expand Down
12 changes: 12 additions & 0 deletions app/utils/urls.py
Expand Up @@ -3,6 +3,18 @@
from .. import settings


def schema(value) -> bool:
return value and "://" in value


def arg(request, default, *names):
for name in names:
value = request.args.get(name)
if value is not None:
return value
return default


def normalize(url: str) -> str:
original = furl(url)
normalized = furl(f"{settings.BASE_URL}{original.path}")
Expand Down
8 changes: 4 additions & 4 deletions app/views/clients.py
Expand Up @@ -4,7 +4,7 @@
from sanic.log import logger
from sanic_openapi import doc

from .. import models, utils
from .. import models, settings, utils

blueprint = Blueprint("Clients", url_prefix="/")

Expand Down Expand Up @@ -46,13 +46,13 @@ async def validate(request):
async def preview(request):
id = request.args.get("template", "_error")
lines = request.args.getlist("lines[]", [])
style = request.args.get("style")
style = request.args.get("style") or settings.DEFAULT_STYLE
return await preview_image(request, id, lines, style)


async def preview_image(request, id: str, lines: list[str], style: str):
id = utils.urls.clean(id)
if "://" in id:
if utils.urls.schema(id):
template = await models.Template.create(id)
if not template.image.exists():
logger.error(f"Unable to download image URL: {id}")
Expand All @@ -63,7 +63,7 @@ async def preview_image(request, id: str, lines: list[str], style: str):
logger.error(f"No such template: {id}")
template = models.Template.objects.get("_error")

if style and "://" not in style:
if not utils.urls.schema(style):
style = style.lower()
await template.check(style)

Expand Down
26 changes: 15 additions & 11 deletions app/views/memes.py
Expand Up @@ -101,7 +101,7 @@ async def automatic(request):
@doc.consumes(
doc.JsonBody(
{
"image_url": str,
"background": str,
"style": str,
"text_lines": [str],
"extension": str,
Expand Down Expand Up @@ -301,8 +301,7 @@ async def render_image(
status = 414

elif id == "custom":
style = request.args.get("style", settings.DEFAULT_STYLE)
url = request.args.get("background") or request.args.get("alt")
url = utils.urls.arg(request, None, "background", "alt")
if url:
template = await models.Template.create(url)
if not template.image.exists():
Expand All @@ -311,14 +310,19 @@ async def render_image(
if url != settings.PLACEHOLDER:
status = 415

# TODO: consolidate style checking for all cases
if "://" in style and not await template.check(style):
logger.error(f"Unable to download image URL: {style}")
status = 415
style = utils.urls.arg(request, settings.DEFAULT_STYLE, "style")
if not utils.urls.schema(style):
style = style.lower()
if not await template.check(style):
if utils.urls.schema(style):
status = 415
elif style != settings.PLACEHOLDER:
status = 422

else:
logger.error("No image URL specified for custom template")
template = models.Template.objects.get("_error")
style = settings.DEFAULT_STYLE
status = 422

else:
Expand All @@ -329,11 +333,11 @@ async def render_image(
if id != settings.PLACEHOLDER:
status = 404

style = request.args.get("style") or request.args.get("alt")
if style and "://" not in style:
style = style.lower()
style = utils.urls.arg(request, settings.DEFAULT_STYLE, "style", "alt")
if not await template.check(style):
if style != settings.PLACEHOLDER:
if utils.urls.schema(style):
status = 415
elif style != settings.PLACEHOLDER:
status = 422

try:
Expand Down
18 changes: 8 additions & 10 deletions app/views/templates.py
Expand Up @@ -87,7 +87,7 @@ async def build(request, id):
@doc.consumes(
doc.JsonBody(
{
"image_url": str,
"background": str,
"style": str,
"text_lines": [str],
"extension": str,
Expand Down Expand Up @@ -123,27 +123,25 @@ async def generate_url(
except KeyError:
return response.json({"error": '"template_id" is required'}, status=400)

text_lines = payload.get("text_lines") or []
style = payload.get("style") or payload.get("alt")
background = payload.get("background") or payload.get("image_url")
extension = payload.get("extension")

status = 201

if template_id:
template = Template.objects.get_or_create(template_id)
url = template.build_custom_url(
request,
payload.get("text_lines") or [],
style=payload.get("style", ""),
extension=payload.get("extension"),
request, text_lines, style=style, extension=extension
)
if not template.valid:
status = 404
template.delete()
else:
template = Template("_custom")
url = template.build_custom_url(
request,
payload.get("text_lines") or [],
background=payload.get("image_url", ""),
style=payload.get("style", ""),
extension=payload.get("extension", ""),
request, text_lines, background=background, style=style, extension=extension
)

url, _updated = await utils.meta.tokenize(request, url)
Expand Down

0 comments on commit e2ffc1f

Please sign in to comment.