Skip to content

Commit

Permalink
handle more edge cases for archives (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
bollwyvl authored Dec 21, 2024
1 parent efe32a5 commit 21bc59d
Show file tree
Hide file tree
Showing 21 changed files with 2,140 additions and 243 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ concurrency:
env:
PYTHONUNBUFFERED: 1
PIP_DISABLE_PIP_VERSION_CHECK: 1
CACHE_EPOCH: 6
URLJSF_PIXI_VERSION: 0.39.2
CACHE_EPOCH: 7
URLJSF_PIXI_VERSION: 0.39.3

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:
env:
PYTHONUNBUFFERED: '1'
PIP_DISABLE_PIP_VERSION_CHECK: '1'
URLJSF_PIXI_VERSION: 0.39.2
URLJSF_PIXI_VERSION: 0.39.3

jobs:
build:
Expand Down
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@
<details>
<summary>Unreleased</summary>

## 0.1.5

> TBD
</details>

## 0.1.5

- [#36]
- adds support for binary files, e.g. uploaded images, in `to_zip_url` template filter
- `to_zip_url` accepts a `name` parameter, to encode into the Data URL
- adds `data_url_file` and `data_url_mime` to extract the file name/MIME type from a
Data URL

[#36]: https://github.com/deathbeds/urljsf/pull/36

## 0.1.4

- [#33]
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,21 @@

Once the data is _validated_, the user sees a button which gets a URL, which can be:

- downloaded as a file
- downloaded as a file (including `.zip` archives)
- opened in a new browser window
- copy and pasted
- submitted to an HTTP endpoint, either by opening a new window, or directly.
- open native applications like email
- opened in registered native applications like email

`urljsf` **doesn't** ship a server, so that part is up to you!

**Site builders** write TOML, JSON, YAML or python, then can use `urljsf` as:
**Builders** write TOML, JSON, YAML, or python, then can use `urljsf` as:

- a drop-in-and-pray [`script`](#js-script)
- a standalone [CLI tool](#command-line)
- a [`sphinx`](#sphinx) or [`mkdocs`](#mkdocs) extension

... to create JavaScript/HTML forms that helps **visitors** provide good data for:
... to create JavaScript/HTML forms that help **visitors** provide good data for:

- pull requests
- issues
Expand Down
26 changes: 26 additions & 0 deletions atest/libraries/archive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Archive keywords for ``urljsf`` acceptance tests."""
# Copyright (C) urljsf contributors.
# Distributed under the terms of the Modified BSD License.

from __future__ import annotations

import zipfile
from pathlib import Path
from urllib.request import urlopen

from robot.libraries.BuiltIn import BuiltIn


def file_in_archive_url_should_match(
data_url: str, member: str, path: str, msg: str | None = None
) -> None:
"""Verify bytes of a file contained in a zip archive match a file on disk."""
bi = BuiltIn()
bi.should_start_with(data_url, "data:", msg="not a Data URL")

with urlopen(data_url) as response, zipfile.ZipFile(response) as zf: # noqa: S310
files = sorted([i.filename for i in zf.filelist])
bi.log(f"files: {files}", level="ERROR")
bi.should_not_be_empty(files)
member_bytes = zf.read(member)
bi.should_be_equal(member_bytes, Path(path).read_bytes(), msg=msg)
2 changes: 1 addition & 1 deletion atest/libraries/server.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Acceptance tests for ``urljsf``."""
"""Keywords for HTTP serving ``urljsf`` acceptance tests."""
# Copyright (C) urljsf contributors.
# Distributed under the terms of the Modified BSD License.

Expand Down
14 changes: 14 additions & 0 deletions atest/suites/01_docs/002_installer.robot
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Library Collections
Library OperatingSystem
Library SeleniumLibrary
Library urllib.parse
Library ../../libraries/archive.py

Suite Setup Setup Urljsf Suite 01_docs/002_installer
Test Setup Open Sphinx Demo installer
Expand All @@ -32,6 +33,8 @@ Create An Installer Pixi Project
Make And Fix A Channel Error
Verify Installer URL
Verify Installer Download
Verify Installer Archive icon.png
Verify Installer Archive icon.svg


*** Keywords ***
Expand Down Expand Up @@ -103,3 +106,14 @@ Verify Installer Download
${expected} = Get TOML Fixture 002_installer.toml
Should Be JSON Equivalent ${from_toml} ${expected} Downloaded TOML not as expected
[Teardown] Remove File ${pt}

Verify Installer Archive
[Documentation] Verify the archive downloads correctly
[Arguments] ${icon}
${icon_file} = Set Variable ${ROOT}${/}docs${/}_static${/}${icon}
Choose File css:#urljsf-0-pixi_icon ${icon_file}
Wait Until Page Contains ${icon}
${url} = Get Element Attribute xpath://pre[3] textContent
${url} = Set Variable ${url.strip()[4:]}
File In Archive URL Should Match ${url} ${icon} ${icon_file}
... Icon did not match
2 changes: 1 addition & 1 deletion docs/.readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build:
# `mambaforge-latest` will now start failing: we just need a working `mamba`
python: mambaforge-23.11
commands:
- mamba install -c conda-forge -c nodefaults pixi==0.39.2
- mamba install -c conda-forge -c nodefaults pixi==0.39.3
- pixi r build-yarn
- pixi r build
- pixi r dist-npm
Expand Down
Binary file added docs/_static/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 50 additions & 17 deletions docs/demos.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"https://raw.githubusercontent.com/spdx/license-list-data/refs/heads/main/"
f"json/{LICENSES.name}"
),
REAL_PIXI_SCHEMA: ("https://pixi.sh/v0.39.2/schema/manifest/schema.json",),
REAL_PIXI_SCHEMA: ("https://pixi.sh/v0.39.3/schema/manifest/schema.json",),
}

FALLBACKS: dict[Path, dict[str, Any]] = {
Expand Down Expand Up @@ -292,6 +292,11 @@ def installer() -> Urljsf:
"items": {"$ref": "#/definitions/a-pixi-package-def"},
"minLength": 1,
},
"icon": {
"description": "an icon in SVG, PNG",
"type": "string",
"format": "data-url",
},
},
"definitions": {
"a-subdir": {"type": "string", "enum": subdirs},
Expand Down Expand Up @@ -321,6 +326,7 @@ def installer() -> Urljsf:
"platforms",
"channels",
"dependencies",
"icon",
]
},
"channels": {"items": {"ui:options": {"label": False}}},
Expand All @@ -343,6 +349,11 @@ def installer() -> Urljsf:
},
}
},
"icon": {
"ui:options": {
"accept": ".png,.svg",
}
},
}

pixi_form_data = {
Expand Down Expand Up @@ -373,22 +384,32 @@ def installer() -> Urljsf:

toml_template = """
{% macro pixi_toml(p, schema=None) %}
{% set deps = [] %}
{% for dep in p.dependencies %}
{% set e = dep.spec %}
{% if dep.channel %}
{% set e = {"version": dep.spec, "channel": dep.channel } %}
{% endif %}
{% set deps = (deps.push([dep.package, e]), deps) %}
{% endfor %}
{% set tool = {} %}
{% if data.pixi.icon %}
{% set tool = {"icon": data.pixi.icon} %}
{% endif %}
{% set PT = {
"project": {
"name": p.name,
"version": p.version,
"platforms": p.platforms,
"channels": p.channels
},
"dependencies": (deps | from_entries)
"dependencies": (deps | from_entries),
"tool": tool
} | prune %}
{% if schema %}
{% for err in PT | schema_errors(schema) %}
Expand Down Expand Up @@ -417,24 +438,36 @@ def installer() -> Urljsf:
data:application/toml,{{ t | urlencode }}
```
_As a `.zip` archive (with a `.gitignore` file and `README.md` and
`pull_request_template.md`):_
{%- set files = [
["pixi.toml", t],
["README.md", "# " ~ data.pixi.name],
[".gitignore", ".pixi"],
[".github", {
"pull_request_template.md": [
"thanks for contributing to " ~ data.pixi.name,
{"level": 9}
]
}]
] -%}
_As a `.zip` archive with:_
- the `pixi.toml`
- a `.gitignore` file
- a `README.md`
- a `.github/pull_request_template.md`
{%- if data.pixi.icon -%}
{%- set regExp = r/name=(.*?);/ -%}
{%- set icon = data.pixi.icon | data_url_file -%}
{%- set files = (files.push([icon, data.pixi.icon]), files) %}
- `{{ icon }}`, an icon `{{ data.pixi.icon | data_url_mime }}` file)
{%- endif %}
```
{{
{
"pixi.toml": t,
"README.md": "# " ~ data.pixi.name,
".gitignore": ".pixi",
".github": {
"pull_request_template.md": [
"thanks for contributing to " ~ data.pixi.name,
{"level": 9}
]
}
}
| to_zip_url(level=0)
files
| from_entries
| prune
| to_zip_url(level=0, name=data.pixi.name ~ ".zip")
}}
```
"""
Expand Down
34 changes: 18 additions & 16 deletions docs/use/advanced/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,22 +135,24 @@ In addition to the [built-in filters][nunjucks-builtins] and the pythonic [`jinj
compatibility layer][jinjacompat], some custom filters are available by default, while
[format-specific](#format-filters) can be lazily loaded.

| [format][ff] | filter | note |
| :----------: | ----------------------- | -------------------------------------------------------------------------------------- |
| | `base64` | encode a string as [`Base64`][base64], useful for encoding arbitrary data in URLs |
| | `from_entries` | build an object from `[key,value]` pairs with [`Object.entries`][entries] |
| | `prune` | recursively remove `null` or empty objects and arrays, useful in TOML |
| | `schema_errors(schema)` | get schema validation errors |
| `json` | `from_json` | parse JSON string |
| `json` | `to_json_url` | build a [Data URL][data-url] for a JSON file |
| `json` | `to_json` | build a JSON string. options: `indent=2` |
| `toml` | `from_toml` | parse a TOML string |
| `toml` | `to_toml_url` | build a [Data URL][data-url] for a TOML file |
| `toml` | `to_toml` | build a TOML string |
| `yaml` | `from_yaml` | parse a YAML string options: [see `yaml` docs][yaml-docs] |
| `yaml` | `to_yaml_url` | build a [Data URL][data-url] for a YAML file |
| `yaml` | `to_yaml` | build a YAML string |
| `zip` | `to_zip_url` | create a [Data URL][data-url] for a zip archive: [see `fflate.unzipSync` docs][fflate] |
| [format][ff] | filter | note |
| :----------: | -------------------------- | -------------------------------------------------------------------------------------- |
| | `base64` | encode a string as [`Base64`][base64], useful for encoding arbitrary data in URLs |
| | `data_url_file` | get the file name from a Data URL |
| | `data_url_mime` | get the MIME type from a Data URL |
| | `from_entries` | build an object from `[key,value]` pairs with [`Object.entries`][entries] |
| | `prune` | recursively remove `null` or empty objects and arrays, useful in TOML |
| | `schema_errors(schema)` | get schema validation errors |
| `json` | `from_json` | parse JSON string |
| `json` | `to_json_url` | build a [Data URL][data-url] for a JSON file |
| `json` | `to_json` | build a JSON string. options: `indent=2` |
| `toml` | `from_toml` | parse a TOML string |
| `toml` | `to_toml_url` | build a [Data URL][data-url] for a TOML file |
| `toml` | `to_toml` | build a TOML string |
| `yaml` | `from_yaml` | parse a YAML string options: [see `yaml` docs][yaml-docs] |
| `yaml` | `to_yaml_url` | build a [Data URL][data-url] for a YAML file |
| `yaml` | `to_yaml` | build a YAML string |
| `zip` | `to_zip_url(name, **opts)` | create a [Data URL][data-url] for a zip archive: [see `fflate.unzipSync` docs][fflate] |

[jinjacompat]: https://mozilla.github.io/nunjucks/api.html#installjinjacompat
[nunjucks-builtins]: https://mozilla.github.io/nunjucks/templating.html#builtin-filters
Expand Down
2 changes: 1 addition & 1 deletion js/demo/json/urljsf.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@
"templates": {
"url": "https://github.com/{{ data.url.repo }}/new/{{ data.url.branch }}?\n{{\n {\"filename\": data.url.filename, \"value\": data.file | to_json } | urlencode | safe\n}}\n",
"submit_button": "Start new JSON Pull Request",
"below_file": "As a JSON Data URI:\n\n```json\n{{ data.file | to_json_url }}\n```\n"
"below_file": "As a JSON Data URL:\n\n```json\n{{ data.file | to_json_url }}\n```\n"
}
}
2 changes: 1 addition & 1 deletion js/demo/toml/urljsf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ https://github.com/{{ data.url.repo }}/new/{{ data.url.branch }}?
"""
submit_button = """Start new TOML Pull Request"""
below_file = """
As a TOML Data URI:
As a TOML Data URL:
```toml
{{ data.file | to_toml_url }}
Expand Down
2 changes: 1 addition & 1 deletion js/demo/yaml/urljsf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ templates:
}}
submit_button: Start new YAML Pull Request
below_file: |
As a YAML Data URI:
As a YAML Data URL:
```yaml
{{ data.file | to_yaml_url }}
Expand Down
1 change: 1 addition & 0 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@rjsf/utils": "^6.0.0-alpha.0",
"@rjsf/validator-ajv8": "^6.0.0-alpha.0",
"@segment/ajv-human-errors": "^2.14.1",
"data-uri-to-buffer": "^6.0.2",
"fflate": "^0.8.2",
"nunjucks": "^3.2.4",
"preact": "^10.24.0",
Expand Down
Loading

0 comments on commit 21bc59d

Please sign in to comment.