Introduction
Welcome to Plato! Plato is a REST API that provides developers with a simple-to-use interface for templating.
In a very summarized flow, you create your own HTML templates, adding placeholders that are later filled with specific data, then you define the data the template requires via a JSON schema structure; after adding the template to your Plato instance, specific endpoints can be used to render the template in various formats with the provided data.
As an example, Plato can be used to define and render custom HTML emails.
Every endpoint available on Plato is described and accompanied by a visual example of how the API responds. For an improved experience when integrating Plato to your code, you can import our libraries:
- Python: plato-helper-py on PyPi TODO Link
- Java: plato-helper-java on Nexus TODO Link
All Python examples used in the examples are from the above library.
When executing the Plato API, a swagger page is provided, which can be used for testing or to get more information about the endpoints. Locally, it is hosted on http://localhost:5000/apidocs/. If you are using our Docker image, you can host it on any port.
Please check our Privacy Policy for more details on data collection. Furthermore, by using the Core API you are agreeing to our Terms and Conditions.
Quick Start
Plato is a microservice that should be added directly to your deployment environment. A docker image is available on Docker Hub, and this guide will show you how to integrate Plato with your existing project.
Requirements
- S3 bucket and respective AWS credentials.
- Docker compose file.
- At least one template. Read on how to create one here.
Steps
Configure S3 Bucket
You will need to create an S3 Bucket if your project doesn't already have one, and fetch its credentials. Plato will need the AWS credentials so it can access the S3 Bucket and load the templates and static content to the machine. The AWS credentials need to be passed into the Plato container as a volume - an example docker-compose file is available later in this tutorial.
Also, please take into consideration that the bucket's structure will need to follow a specific set of rules:
- You require a main, base directory for all the templating files. This can be called anything you want (ex: plato), and does not have to be localized in the base bucket directory.
- Inside the main directory, two subdirectories are required with very specific names and structures:
- templates directory, where the HTML files of the templates are stored. Each template HTML file is stored within a folder that is named the same as the template ID. Furthermore, the HTML file should also be named the same and should not contain an extension (.html).
- static directory, where the template static files are stored. Similarly to the templates folder, all static files for a template are stored in a folder with the same name as the template ID. The static content can have any name or structure, as long as they are correctly imported in the HTML file.
For example:
plato/static/example_template/image.pngplato/templates/example_template/example_template
Docker configuration
plato:
image: plato-api:<VERSION>
environment:
DATA_DIR: /plato-data/
TEMPLATE_DIRECTORY: /plato-data/templating/
DB_HOST: plato-database
DB_PORT: 5432
DB_USERNAME: <USER>
DB_PASSWORD: <PASSWORD>
DB_DATABASE: <DB>
S3_BUCKET: <PLATO_BUCKET>
S3_TEMPLATE_DIR: <PLATO_BASE_DIRECTORY>
depends_on:
- plato-database
volumes:
- <AWS_CREDENTIALS>:/root/.aws
ports:
- 3000:80
plato-database:
image: postgres:<VERSION>
volumes:
- <DATABASE_DIR>/plato-db:/data/db
environment:
POSTGRES_USER: <USER>
POSTGRES_PASSWORD: <PASSWORD>
POSTGRES_DB: <DB>
PGDATA: /data/db/
ports:
- 5432:5432
Plato requires a Postgres database to work with; we recommend configuring it via Docker, but you can also use any external database already available on your project. Plato is currently developed with Postgres version 9.6.3, and has been successfully used with Postgres up to version 12.11. The database contains a single table called Template where all templates are stored. The Alembic table can be ignored, since it is used for migrations.
You can directly copy the accompanying docker-compose configuration to your project, but make sure to fill in the missing variables:
- S3_BUCKET: The name of the bucket on S3 to be used
- S3_TEMPLATE_DIR: The base directory for Plato templates on the S3 bucket. The full path to the folder is required if it is not in the base directory. Per our previous example, this value would be "plato".
- Database credentials (USER, PASSWORD, DB)
You should not change the values for the DATA_DIR and TEMPLATE_DIRECTORY variables. All others can be changed and adapted to fit accordingly to your project's configuration. Also note that a volume for AWS credentials is created, so you have to indicate where the credentials are in the running environment.
You can now run both docker containers and the Plato API swagger will be available on http://localhost:3000/apidocs. 3000 is the default port defined on the docker-compose file, but it can be changed for anything else.
Add Templates to Plato
{
"title": "student-diploma",
"schema": {
"type": "object",
"required": [
"recipient_name",
"certificate_number"
],
"properties": {
"certificate_number": {
"type": "string"
},
"qr_code": {
"type": "string"
},
"recipient_name": {
"type": "string"
}
}
},
"type": "text/html",
"metadata": {
"qr_entries": [
"qr_code"
]
},
"example_composition": {
"qr_code": "https://vizidox.com",
"recipient_name": "Alan Turing",
"certificate_number": "123ABC"
},
"tags": [
"2022"
]
}
If you have already added your template files to the Plato storage, per the previous instructions on this guide, and you have both the database and the API up and running, then the only thing missing is to create your JSON Schema and populate the database with the template data.
To define the JSON Schema and other data, you need to define what fields in the HTML are to be filled in, and specify their types, if they are required, etc. Check the JSON Schema specification to learn the expected syntax. Regarding the rest of the template data:
- Title: The template name/ID
- Type: The template type. Currently, only "application/html" is accepted.
- Metadata: Fully optional field that can be left empty, but can be used to define QR fields in the HTML. To do so, you need to add a "qr_entries" array to the metadata field, containing a list of all template fields that contain an URL to be transformed into QR codes. These fields should be in a JMESPath friendly sequence such as, for example, "course.organization.contact.website_url".
- Example Composition: A JSON containing example values for the fields in the template. Can be used to quickly generate an example file of the template.
- Tags: Any additional details that can be used to identify the template.
To populate the templates on the Plato database, you can follow two different approaches:
Example query to insert a template on the database
INSERT INTO public."template"
(id, "schema", "type", metadata, tags, example_composition)
VALUES('student-diploma', '{"type": "object", "required": ["recipient_name", "certificate_number"], "properties": {"qr_code": {"type": "string"}, "recipient_name": {"type": "string"}, "certificate_number": {"type": "string", "title": "Certificate Number"}}}'::jsonb, 'text/html'::public.template_mime_type, '{"qr_entries": ["qr_code"]}'::jsonb, '{}', '{"qr_code": "https://vizidox.com", "recipient_name": "Alan Turing", "certificate_number": "C5678"}'::jsonb);
Directly insert them in the database via a SQL insert query. The template table consists of (similarly to the JSON presented to the right):
Column Type Description id string Template ID schema jsonb JSON Schema of the template type string MIME type for template type metadata jsonb JSON dictionary for arbitrary data useful for you tags Array[String] List of tags example_composition jsonb JSON dictionary with a compose example for the template
An example insertion query can be found on the right side of this page.
docker-compose run --rm plato flask <command>
Commands:
db Perform database migrations.
export_template Export new template to file Args: output: output...
refresh
register_new_template Imports new template from json file and inserts it...
routes Show the routes for the app.
run Run a development server.
shell Run a shell in the app context.
- Use the Plato CLI. You have to enter the container with the run command, and then execute the register_new_template command, according to instructions to the right.
You can also run
docker-compose run --rm plato flask --helpfor information on the available commands. The input JSON file that the register_new_template command requires has the JSON structure found on the right side of the page. Note that the title corresponds directly to the template ID.
After inserting the template data in the database, the Plato API needs to be refreshed. To do so, you can either restart the Plato container manually, or run the refresh command via the Plato CLI. At this time, all template files will be downloaded to Plato's local temporary storage (the aforementioned DATA_DIR), so depending on the number of templates that are configured, this might take some time. Every time Plato is restarted, these files are re-downloaded.
Create Template
A Plato template contains two parts:
- An HTML file (plus any added static files), which will be used to produce the final, composed file.
- A json schema structure, defining all fields required by the template to be composed.
To create a template, first you need to create your base HTML file. There is nothing special about this file, but there are some details that you should bear in mind:
- It is ideal for the template to be a single HTML file, although importing is possible.
- The static content can be imported as well, but composing the template into HTML will not work. For that scenario, it is ideal to embed all static content directly on the single HTML file.
When you are done with your template HTML file, you need to identify the fields that are to be placeholders for the template. Plato uses Jinja to compose the variable values on to the placeholders. For example, if you have want to fill a specific place on the template with a user's name, then you write {{ p.username }}, where username is a field on the JSON schema. For more details on how you can customize your template with Jinja, check the template designer page.
There are also some filters available that allow you to customize the appearance of the values added to the placeholders. For example, if you pass in a string and you want it converted to lower-case, then write the following: {{ p.name | lower }}. Multiple filters can be chained, and they are executed from the right to the left. All [Jinja pre-defined filters}{https://jinja.palletsprojects.com/templates/#filters} can be used, as well as some custom filters that have been added:
- format_dates, to format any date. Use with no argument to format to the default option (1 January 2020), or pass in any valid babel format for other options.
- num_to_ordinal, change a cardinal number (in string or int) to ordinal format. For example, '1' to '1st' or '16' to '16th'.
- nth, returns the suffix of an ordinal number. For example, '1' returns 'st' and '16' returns 'th'.
After you are done with the HTML file, then create the corresponding JSON Schema, and add everything to Plato, according to instructions on the Quick Start guide.
Template Management
Create a Template
curl -X POST "http://localhost:5000/template/create" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "zipfile=<file>>;type=application/zip" -F "template_details=<details>>"
template = plato_client.create_template(template, template_details)
class TemplateInfo(NamedTuple):
template_id: str
template_schema: dict
type: str
metadata: dict
tags: List[str]
Create a new template on Plato. Adds the provided HTML and static files to Plato's long term storage, and the details are added to the database.
| Parameter | Type | Optional | Description |
|---|---|---|---|
| zipfile | Form data | No | Archive containing the template HTML and static files |
| template_details | Form data | No | Details of the template, in json format |
The zipfile parameter should be a ZIP archive with a structure similar the Plato's permanent storage:
- "template" folder containing an HTML file. The HTML file should have the name of the template, and no extension. For example, if the user is creating a templated named "example-template", then the html file should be named "example-template".
- "static" folder, containing any static files required by the template. If no files are required, the folder should be empty.
The template_details parameter should be in json format and include all relevant details for the template creation:
| Name | Description |
|---|---|
| id | Template identifier. |
| schema | Template JSON schema. |
| type | Type of the template. Only "text/html" is accepted. |
| metadata | Any additional metadata such as QR Code fields. |
| example_composition | A JSON containing an example composition for the template. |
| tags | Extra tags to help identify the template |
HTTP Request
POST http://localhost:5000/template/create
Returns
If successful, the HTTP response is a 201 CREATED, along with the created template.
Errors
| code | Description |
|---|---|
| 400 | Provided ZIP file does not have the correct structure |
| 409 | The template already exists |
| 415 | The provided file is not a ZIP file |
Update a Template
curl -X PUT "http://localhost:5000/template/<template_id>/update" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "zipfile=<file>>;type=application/zip" -F "template_details=<details>>"
template = plato_client.update_template(template_id, file_stream, template_details)
class TemplateInfo(NamedTuple):
template_id: str
template_schema: dict
type: str
metadata: dict
tags: List[str]
Update an existing template on Plato. The template is fully updated, including the HTML file, static content and database entry.
| Parameter | Type | Optional | Description |
|---|---|---|---|
| zipfile | Form data | No | Archive containing the template HTML and static files |
| template_details | Form data | No | Details of the template, in json format |
The format of the zipfile and the template_details parameters is the same as in the previous endpoint.
HTTP Request
PUT http://localhost:5000/template/<template_id>/update
Returns
If successful, the HTTP response is a 200 OK, along with the updated template.
Errors
| code | Description |
|---|---|
| 400 | Provided ZIP file does not have the correct structure |
| 404 | Template not found |
| 415 | The provided file is not a ZIP file |
Update Template Details
curl -X PATCH "http://localhost:5000/template/<template_id>/update_details" -H "accept: application/json" -H "Content-Type: application/json" -d <details>
template = plato_client.update_template_details(template_id, template_details)
class TemplateInfo(NamedTuple):
template_id: str
template_schema: dict
type: str
metadata: dict
tags: List[str]
Update the database details for an existing template on Plato. No changes are done to the HTML and static content of the template.
| Parameter | Type | Optional | Description |
|---|---|---|---|
| template_details | Body | No | Details of the template, in json format |
The format of the template_details parameter is the same as in the template creation endpoint.
HTTP Request
PATCH http://localhost:5000/template/<template_id>/update_details
Returns
If successful, the HTTP response is a 200 OK, along with the updated template.
Errors
| code | Description |
|---|---|
| 400 | Input not in correct form |
| 404 | Template not found |
Template
The Template Object
{
"metadata": {
"qr_entries": [
"qr_code"
]
},
"tags": ["2022"],
"template_id": "student-diploma",
"template_schema": {
"properties": {
"certificate_number": {
"title": "Certificate Number",
"type": "string"
},
"qr_code": {
"type": "string"
},
"recipient_name": {
"type": "string"
}
},
"required": [
"recipient_name",
"certificate_number"
],
"type": "object"
},
"type": "text/html"
}
A Template object is none other than an HTML template itself, including the json schema used to fill in the defined variables in the HTML file.
| Field | Description |
|---|---|
| template_id | Unique identifier of the template. |
| metadata | Extra metadata used by the template. For example, can be used to define QR Code fields. |
| tags | Any additional tags that can identify the template. |
| template_schema | The json schema of the template. |
| type | The type of the template (HTML only). |
| example_composition | A json containing example values to fill in the template. |
Get All Templates
curl -X GET "http://localhost:5000/templates/" -H "accept: application/json"
templates = plato_client.templates(tags)
class TemplateInfo(NamedTuple):
template_id: str
template_schema: dict
type: str
metadata: dict
tags: List[str]
Retrieves all templates available on the database. Has a single query parameter, "tags", that can be used multiple times in the same requests. All the tags provided are used to filter for templates that contain at least one of the tags.
HTTP Request
GET http://localhost:5000/templates/?tags=abc&tags=xyz
Returns
If successful, the HTTP response is a 200 OK, along with all templates.
Get A Template
curl -X GET "http://localhost:5000/templates/<template_id>" -H "accept: application/json"
Retrieves a specific template and its details.
HTTP Request
GET "http://localhost:5000/templates/<template_id>"
Returns
If successful, the HTTP response is a 200 OK, along with the template.
Errors
| code | Description |
|---|---|
| 404 | Template not found |
Compose File
curl -X POST "http://localhost:5000/template/<template_id>/compose" -H "accept: <mime_type>" -H "Content-Type: application/json" -d "{\"recipient_name\": \"Alan Turing\"}"
file = plato_client.compose(template_id, compose_data, mime_type, page, resize_height, resize_width)
Composes a template into a file of a specific type by filling in the placeholders with the intended data. The type of the file to compose can be defined by the accept header, and is expected to be in MIME format. It is currently possible to generate a file of three different types:
- HTML: text/html
- PDF: application/pdf
- PNG: image/png
Other parameters include:
| Parameter | Type | Optional | Description |
|---|---|---|---|
| template_id | Path | No | ID of the template to compose. |
| schema | Body | No | Json containing the data to add to the template, according to template schema. |
| accept | Header | No | Type of file to create. |
| page | query | Yes | Specific page of the template to compose. If none is given, all pages are composed. Defaults to one if an image type is chosen. |
| height | query | Yes | Height of the file to compose, if image type is chosen. |
| width | query | Yes | Weight of the file to compose, if image type is chosen. |
HTTP Request
POST http://localhost:5000/template/<template_id>/compose
Returns
If successful, the HTTP response is a 200 OK, along with the file.
Errors
| code | Description |
|---|---|
| 400 | Invalid compose data for template schema |
| 404 | Template not found |
| 406 | Unsupported MIME type for file |
Compose Example
curl -X GET "http://localhost:5000/template/<template_id>/example" -H "accept: <mime_type>
file = plato_client.compose(template_id, compose_data, mime_type, page, resize_height, resize_width)
Composes a template into an example file of a specific type. The placeholders are filled in with example data that is configured directly in the database. The type of the file to compose can be defined by the accept header, and is expected to be in MIME format. It is currently possible to generate a file of three different types:
- HTML: text/html
- PDF: application/pdf
- PNG: image/png
Other parameters include:
| Parameter | Type | Optional | Description |
|---|---|---|---|
| template_id | Path | No | ID of the template to compose. |
| accept | Header | No | Type of file to create. |
| page | query | Yes | Specific page of the template to compose. If none is given, all pages are composed. Defaults to one if an image type is chosen. |
| height | query | Yes | Height of the file to compose, if image type is chosen. |
| width | query | Yes | Weight of the file to compose, if image type is chosen. |
HTTP Request
GET http://localhost:5000/template/<template_id>/example
Returns
If successful, the HTTP response is a 200 OK, along with the example file.
Errors
| code | Description |
|---|---|
| 404 | Template not found |
| 406 | Unsupported MIME type for file |