Skip to content

Commit 77191cc

Browse files
authored
Is91/review email templates (⚠️ devops) (ITISFoundation#3283)
1 parent 3a9e073 commit 77191cc

31 files changed

+975
-236
lines changed

.vscode/extensions.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"recommendations": [
3+
"eamodio.gitlens",
4+
"ms-python.python",
5+
"samuelcolvin.jinjahtml",
6+
]
7+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""adds jinja template table
2+
3+
Revision ID: 33eafc1aa8ef
4+
Revises: a8f0bacbbaef
5+
Create Date: 2022-08-25 11:30:09.190686+00:00
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
11+
# revision identifiers, used by Alembic.
12+
revision = "33eafc1aa8ef"
13+
down_revision = "a8f0bacbbaef"
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
# ### commands auto generated by Alembic - please adjust! ###
20+
op.create_table(
21+
"jinja2_templates",
22+
sa.Column("name", sa.String(), nullable=False),
23+
sa.Column("content", sa.Text(), nullable=False),
24+
sa.Column(
25+
"created", sa.DateTime(), server_default=sa.text("now()"), nullable=False
26+
),
27+
sa.Column(
28+
"modified", sa.DateTime(), server_default=sa.text("now()"), nullable=False
29+
),
30+
sa.PrimaryKeyConstraint("name", name="jinja2_templates_name_pk"),
31+
)
32+
# ### end Alembic commands ###
33+
34+
35+
def downgrade():
36+
# ### commands auto generated by Alembic - please adjust! ###
37+
op.drop_table("jinja2_templates")
38+
# ### end Alembic commands ###
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""product issue cols nullable
2+
3+
Revision ID: a8f0bacbbaef
4+
Revises: e15cc5042999
5+
Create Date: 2022-08-24 13:33:30.104287+00:00
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
11+
# revision identifiers, used by Alembic.
12+
revision = "a8f0bacbbaef"
13+
down_revision = "e15cc5042999"
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
# ### commands auto generated by Alembic - please adjust! ###
20+
op.alter_column(
21+
"products",
22+
"issues_login_url",
23+
existing_type=sa.VARCHAR(),
24+
nullable=True,
25+
existing_server_default=sa.text(
26+
"'https://github.com/ITISFoundation/osparc-simcore/issues'::character varying"
27+
),
28+
)
29+
op.alter_column(
30+
"products",
31+
"issues_new_url",
32+
existing_type=sa.VARCHAR(),
33+
nullable=True,
34+
existing_server_default=sa.text(
35+
"'https://github.com/ITISFoundation/osparc-simcore/issues/new'::character varying"
36+
),
37+
)
38+
# ### end Alembic commands ###
39+
40+
41+
def downgrade():
42+
# ### commands auto generated by Alembic - please adjust! ###
43+
op.alter_column(
44+
"products",
45+
"issues_new_url",
46+
existing_type=sa.VARCHAR(),
47+
nullable=False,
48+
existing_server_default=sa.text(
49+
"'https://github.com/ITISFoundation/osparc-simcore/issues/new'::character varying"
50+
),
51+
)
52+
op.alter_column(
53+
"products",
54+
"issues_login_url",
55+
existing_type=sa.VARCHAR(),
56+
nullable=False,
57+
existing_server_default=sa.text(
58+
"'https://github.com/ITISFoundation/osparc-simcore/issues'::character varying"
59+
),
60+
)
61+
# ### end Alembic commands ###
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""add template col to product table
2+
3+
Revision ID: e5f3167d7277
4+
Revises: 33eafc1aa8ef
5+
Create Date: 2022-08-25 11:32:56.467413+00:00
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
11+
# revision identifiers, used by Alembic.
12+
revision = "e5f3167d7277"
13+
down_revision = "33eafc1aa8ef"
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
# ### commands auto generated by Alembic - please adjust! ###
20+
op.add_column(
21+
"products", sa.Column("registration_email_template", sa.String(), nullable=True)
22+
)
23+
op.create_foreign_key(
24+
"fk_jinja2_templates_name",
25+
"products",
26+
"jinja2_templates",
27+
["registration_email_template"],
28+
["name"],
29+
onupdate="CASCADE",
30+
ondelete="SET NULL",
31+
)
32+
# ### end Alembic commands ###
33+
34+
35+
def downgrade():
36+
# ### commands auto generated by Alembic - please adjust! ###
37+
op.drop_constraint("fk_jinja2_templates_name", "products", type_="foreignkey")
38+
op.drop_column("products", "registration_email_template")
39+
# ### end Alembic commands ###
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
""" Collection of jinja2 templates for customizable docs (e.g. emails, sites)
2+
3+
"""
4+
5+
import sqlalchemy as sa
6+
from sqlalchemy.sql import func
7+
8+
from .base import metadata
9+
10+
jinja2_templates = sa.Table(
11+
"jinja2_templates",
12+
metadata,
13+
sa.Column(
14+
"name",
15+
sa.String,
16+
nullable=False,
17+
doc="Uniquely identifies a template",
18+
),
19+
sa.Column(
20+
"content",
21+
sa.Text,
22+
nullable=False,
23+
doc="Text of template. Should be parsable with jinja2",
24+
),
25+
sa.Column(
26+
"created",
27+
sa.DateTime(),
28+
nullable=False,
29+
server_default=func.now(),
30+
doc="Timestamp auto-generated upon creation",
31+
),
32+
sa.Column(
33+
"modified",
34+
sa.DateTime(),
35+
nullable=False,
36+
server_default=func.now(),
37+
onupdate=func.now(),
38+
doc="Automaticaly updates on modification of the row",
39+
),
40+
sa.PrimaryKeyConstraint("name", name="jinja2_templates_name_pk"),
41+
)

packages/postgres-database/src/simcore_postgres_database/models/products.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from sqlalchemy.sql import func
1010

1111
from .base import metadata
12+
from .jinja2_templates import jinja2_templates
1213

1314
products = sa.Table(
1415
"products",
@@ -65,23 +66,37 @@
6566
sa.Column(
6667
"issues_login_url",
6768
sa.String,
68-
nullable=False,
69+
nullable=True,
6970
server_default="https://github.com/ITISFoundation/osparc-simcore/issues",
70-
doc="URL to login in the issue tracker site",
71+
doc="URL to login in the issue tracker site"
72+
"NOTE: Set nullable because some POs consider that issue tracking is optional in some products.",
7173
),
7274
sa.Column(
7375
"issues_new_url",
7476
sa.String,
75-
nullable=False,
77+
nullable=True,
7678
server_default="https://github.com/ITISFoundation/osparc-simcore/issues/new",
77-
doc="URL to create a new issue for this product (e.g. fogbugz new case, github new issues)",
79+
doc="URL to create a new issue for this product (e.g. fogbugz new case, github new issues)"
80+
"NOTE: Set nullable because some POs consider that issue tracking is optional in some products.",
7881
),
7982
sa.Column(
8083
"feedback_form_url",
8184
sa.String,
8285
nullable=True,
8386
doc="URL to a feedback form (e.g. google forms etc)",
8487
),
88+
sa.Column(
89+
"registration_email_template",
90+
sa.String,
91+
sa.ForeignKey(
92+
jinja2_templates.c.name,
93+
name="fk_jinja2_templates_name",
94+
ondelete="SET NULL",
95+
onupdate="CASCADE",
96+
),
97+
nullable=True,
98+
doc="Custom jinja2 template for registration email",
99+
),
85100
sa.Column(
86101
"created",
87102
sa.DateTime(),

packages/postgres-database/tests/test_models_products.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
# pylint: disable=unused-argument
55

66

7+
from pathlib import Path
78
from typing import Callable
89

910
import pytest
1011
import sqlalchemy as sa
1112
from aiopg.sa.engine import Engine
1213
from aiopg.sa.exc import ResourceClosedError
1314
from aiopg.sa.result import ResultProxy, RowProxy
15+
from simcore_postgres_database.models.jinja2_templates import jinja2_templates
1416
from simcore_postgres_database.webserver_models import products
1517

1618

@@ -62,3 +64,79 @@ async def test_load_products(
6264
assert {
6365
row[products.c.name]: row[products.c.host_regex] for row in rows
6466
} == product_sample
67+
68+
69+
async def test_jinja2_templates_table(
70+
pg_engine: Engine, osparc_simcore_services_dir: Path
71+
):
72+
73+
templates_common_dir = (
74+
osparc_simcore_services_dir
75+
/ "web/server/src/simcore_service_webserver/templates/common"
76+
)
77+
78+
async with pg_engine.acquire() as conn:
79+
80+
templates = []
81+
# templates table
82+
for p in templates_common_dir.glob("*.jinja2"):
83+
name = await conn.scalar(
84+
jinja2_templates.insert()
85+
.values(name=p.name, content=p.read_text())
86+
.returning(jinja2_templates.c.name)
87+
)
88+
templates.append(name)
89+
90+
# choose one
91+
registration_email_template = next(n for n in templates if "registration" in n)
92+
93+
# products table
94+
for params in [
95+
{
96+
"name": "osparc",
97+
"host_regex": r"^osparc.",
98+
"registration_email_template": registration_email_template,
99+
},
100+
{
101+
"name": "s4l",
102+
"host_regex": r"(^s4l[\.-])|(^sim4life\.)",
103+
"registration_email_template": registration_email_template,
104+
},
105+
{
106+
"name": "tis",
107+
"host_regex": r"(^ti.[\.-])|(^ti-solution\.)",
108+
},
109+
]:
110+
# aiopg doesn't support executemany!!
111+
await conn.execute(
112+
products.insert().values(**params),
113+
)
114+
115+
# prints those products having customized templates
116+
j = products.join(jinja2_templates)
117+
stmt = sa.select([products.c.name, jinja2_templates.c.name]).select_from(j)
118+
119+
result: ResultProxy = await conn.execute(stmt)
120+
assert result.rowcount == 2
121+
assert await result.fetchall() == [
122+
("osparc", "registration_email.jinja2"),
123+
("s4l", "registration_email.jinja2"),
124+
]
125+
126+
assert (
127+
await conn.scalar(
128+
sa.select([jinja2_templates.c.content])
129+
.select_from(j)
130+
.where(products.c.name == "s4l")
131+
)
132+
is not None
133+
)
134+
135+
assert (
136+
await conn.scalar(
137+
sa.select([jinja2_templates.c.content])
138+
.select_from(j)
139+
.where(products.c.name == "tis")
140+
)
141+
is None
142+
)

services/web/server/setup.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import re
22
import sys
33
from pathlib import Path
4-
from typing import Set
54

65
from setuptools import find_packages, setup
76

87

9-
def read_reqs(reqs_path: Path) -> Set[str]:
8+
def read_reqs(reqs_path: Path) -> set[str]:
109
return {
1110
r
1211
for r in re.findall(
@@ -52,7 +51,7 @@ def read_reqs(reqs_path: Path) -> Set[str]:
5251
"": [
5352
"api/v0/openapi.yaml",
5453
"api/v0/schemas/*.json",
55-
"templates/**/*.html",
54+
"templates/**/*.jinja2",
5655
]
5756
},
5857
entry_points={

services/web/server/src/simcore_service_webserver/application_settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ def valid_log_level(cls, value) -> str:
240240
def get_healthcheck_timeout_in_seconds(cls, v):
241241
# Ex. HEALTHCHECK --interval=5m --timeout=3s
242242
if isinstance(v, str):
243+
factor = 1 # defaults on s
243244
if v.endswith("s"):
244245
factor = 1
245246
elif v.endswith("m"):

0 commit comments

Comments
 (0)