Skip to content

Commit

Permalink
feat: Add command to generate JSON schema for the package
Browse files Browse the repository at this point in the history
There's a fundamental issue: the app works off of pydantic models. They
are the source of truth. We'd like to provide an ancv-specific JSON
Schema for users to fill out their JSONs against/with. IDEs like VSCode
would then help with auto-completion. Nice win!

Ideally, we'd like to auto-generate the JSON Schema from the pydantic
models. This has the downside of suddenly including a verbose version of
the (licensed) JSON Resume schema instead of just referencing it via
URL. It also leads to inconsistencies.

We could also hand-write a JSON Schema and auto-generate the Pydantic
models from it (the inverse way). This has countless more problems, like
the weird ISO-8601 regex-based definition in the JSON Resume Schema,
which we had changed to `date` types in the pydantic models.

As such, it was deemed most suitable to hand-write *both* the JSON
Schema for ancv as well as the pydantic models. This leads to (some
minor) code duplication and the risk of the two becoming asynchronous.
If this proves a pain in the future, look into this issue again. For
now, the pain of keeping the two in sync is much greater.
  • Loading branch information
alexpovel committed Sep 20, 2022
1 parent 432311a commit ea26b22
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 0 deletions.
72 changes: 72 additions & 0 deletions ancv/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,78 @@ def list() -> None:
print(tree)


@app.command()
def generate_schema() -> None:
"""Generates and prints the current JSON schema.
ATTENTION: This schema is defined manually, independently of the actual models
contained within this package. As such, the two *might* end up out of sync. This
approach was chosen as a temporary solution, since syncing the JSON Schema and the
pydantic models is a lot of work with a lot of tiny blockers.
"""

import json

from ancv.reflection import METADATA
from ancv.visualization.templates import Template
from ancv.visualization.themes import THEMES
from ancv.visualization.translations import TRANSLATIONS

schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"allOf": [
{
"$ref": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json"
},
{
"type": "object",
"properties": {
"meta": {
"allOf": [
{
"$ref": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json#/properties/meta"
}
],
"properties": {
METADATA.name: {
"type": "object",
"description": f"{METADATA.name}-specific ({METADATA.home_page}) properties",
"properties": {
"template": {
"type": "string",
"description": "The template (ordering, alignment, positioning, ...) to use",
"enum": sorted(Template.subclasses().keys()),
},
"theme": {
"type": "string",
"description": "The theme (colors, emphasis, ...) to use",
"enum": sorted(THEMES.keys()),
},
"language": {
"type": "string",
"description": "The language aka translation (for section titles like 'Education' etc.) to use",
"enum": sorted(TRANSLATIONS.keys()),
},
"ascii_only": {
"type": "boolean",
"description": "Whether to only use ASCII characters in the template (you are responsible for not using non-ASCII characters in your resume)",
},
"dec31_as_year": {
"type": "boolean",
"description": "Whether to display dates of 'December 31st of some year' as that year only, without month or day info",
},
},
}
},
}
},
},
],
}

print(json.dumps(schema, indent=4))


@app.callback()
def main(
verbose: bool = typer.Option(
Expand Down
64 changes: 64 additions & 0 deletions schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"allOf": [
{
"$ref": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json"
},
{
"type": "object",
"properties": {
"meta": {
"allOf": [
{
"$ref": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json#/properties/meta"
}
],
"properties": {
"ancv": {
"type": "object",
"description": "ancv-specific (https://ancv.io) properties",
"properties": {
"template": {
"type": "string",
"description": "The template (ordering, alignment, positioning, ...) to use",
"enum": [
"Sequential"
]
},
"theme": {
"type": "string",
"description": "The theme (colors, emphasis, ...) to use",
"enum": [
"basic",
"grayscale",
"hendrix",
"lollipop",
"plain"
]
},
"language": {
"type": "string",
"description": "The language aka translation (for section titles like 'Education' etc.) to use",
"enum": [
"de",
"en",
"es",
"fr"
]
},
"ascii_only": {
"type": "boolean",
"description": "Whether to only use ASCII characters in the template (you are responsible for not using non-ASCII characters in your resume)"
},
"dec31_as_year": {
"type": "boolean",
"description": "Whether to display dates of 'December 31st of some year' as that year only, without month or day info"
}
}
}
}
}
}
}
]
}
5 changes: 5 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ def test_list_exists() -> None:
assert result.exit_code == 0


def test_generate_schema_exists() -> None:
result = RUNNER.invoke(app, ["generate-schema"])
assert result.exit_code == 0


@pytest.mark.parametrize("filename", RESUMES.values())
# All resumes as a single fixture wouldn't be too bad either but doesn't work:
# https://stackoverflow.com/q/56672179/11477374
Expand Down

0 comments on commit ea26b22

Please sign in to comment.