Store WhatsApp templates in database for persistence across rebuilds
This commit is contained in:
parent
ca9375a779
commit
74594b2411
@ -585,7 +585,7 @@ async def send_guest_message(
|
|||||||
phone = message_req.phone or guest.phone
|
phone = message_req.phone or guest.phone
|
||||||
|
|
||||||
try:
|
try:
|
||||||
service = get_whatsapp_service()
|
service = get_whatsapp_service(db)
|
||||||
result = await service.send_text_message(phone, message_req.message)
|
result = await service.send_text_message(phone, message_req.message)
|
||||||
return result
|
return result
|
||||||
except WhatsAppError as e:
|
except WhatsAppError as e:
|
||||||
@ -635,7 +635,7 @@ async def broadcast_whatsapp_message(
|
|||||||
failed = []
|
failed = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
service = get_whatsapp_service()
|
service = get_whatsapp_service(db)
|
||||||
for guest in guests:
|
for guest in guests:
|
||||||
try:
|
try:
|
||||||
result = await service.send_text_message(guest.phone, message)
|
result = await service.send_text_message(guest.phone, message)
|
||||||
@ -667,16 +667,17 @@ async def broadcast_whatsapp_message(
|
|||||||
# WhatsApp Template Registry Endpoints
|
# WhatsApp Template Registry Endpoints
|
||||||
# ============================================
|
# ============================================
|
||||||
@app.get("/whatsapp/templates")
|
@app.get("/whatsapp/templates")
|
||||||
async def get_whatsapp_templates():
|
async def get_whatsapp_templates(db: Session = Depends(get_db)):
|
||||||
"""
|
"""
|
||||||
Return all registered WhatsApp templates (built-in + custom) for the frontend dropdown.
|
Return all registered WhatsApp templates (built-in + custom) for the frontend dropdown.
|
||||||
"""
|
"""
|
||||||
return {"templates": list_templates_for_frontend()}
|
return {"templates": list_templates_for_frontend(db)}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/whatsapp/templates")
|
@app.post("/whatsapp/templates")
|
||||||
async def create_whatsapp_template(
|
async def create_whatsapp_template(
|
||||||
body: dict,
|
body: dict,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
current_user_id = Depends(get_current_user_id)
|
current_user_id = Depends(get_current_user_id)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -733,7 +734,7 @@ async def create_whatsapp_template(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
add_custom_template(key, template)
|
add_custom_template(db, key, template)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=409, detail=str(e))
|
raise HTTPException(status_code=409, detail=str(e))
|
||||||
|
|
||||||
@ -743,6 +744,7 @@ async def create_whatsapp_template(
|
|||||||
@app.delete("/whatsapp/templates/{key}")
|
@app.delete("/whatsapp/templates/{key}")
|
||||||
async def delete_whatsapp_template(
|
async def delete_whatsapp_template(
|
||||||
key: str,
|
key: str,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
current_user_id = Depends(get_current_user_id)
|
current_user_id = Depends(get_current_user_id)
|
||||||
):
|
):
|
||||||
"""Delete a custom template by key (built-in templates cannot be deleted)."""
|
"""Delete a custom template by key (built-in templates cannot be deleted)."""
|
||||||
@ -750,7 +752,7 @@ async def delete_whatsapp_template(
|
|||||||
raise HTTPException(status_code=403, detail="Not authenticated")
|
raise HTTPException(status_code=403, detail="Not authenticated")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
delete_custom_template(key)
|
delete_custom_template(db, key)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=403, detail=str(e))
|
raise HTTPException(status_code=403, detail=str(e))
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
@ -814,7 +816,7 @@ async def send_wedding_invitation_single(
|
|||||||
_gl_base = (event.guest_link or "https://invy.dvirlabs.com/guest").split("?")[0].rstrip("/")
|
_gl_base = (event.guest_link or "https://invy.dvirlabs.com/guest").split("?")[0].rstrip("/")
|
||||||
guest_link = f"{_gl_base}/{event_id}"
|
guest_link = f"{_gl_base}/{event_id}"
|
||||||
|
|
||||||
service = get_whatsapp_service()
|
service = get_whatsapp_service(db)
|
||||||
result = await service.send_wedding_invitation(
|
result = await service.send_wedding_invitation(
|
||||||
to_phone=to_phone,
|
to_phone=to_phone,
|
||||||
guest_name=guest_name,
|
guest_name=guest_name,
|
||||||
@ -882,7 +884,7 @@ async def send_wedding_invitation_bulk(
|
|||||||
results = []
|
results = []
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
service = get_whatsapp_service()
|
service = get_whatsapp_service(db)
|
||||||
|
|
||||||
for guest in guests:
|
for guest in guests:
|
||||||
try:
|
try:
|
||||||
@ -949,7 +951,7 @@ async def send_wedding_invitation_bulk(
|
|||||||
# Auto-inject guest_name_key + event_id for url_button templates
|
# Auto-inject guest_name_key + event_id for url_button templates
|
||||||
try:
|
try:
|
||||||
from whatsapp_templates import get_template as _get_tpl
|
from whatsapp_templates import get_template as _get_tpl
|
||||||
_tpl_def = _get_tpl(request_body.template_key or "wedding_invitation")
|
_tpl_def = _get_tpl(db, request_body.template_key or "wedding_invitation")
|
||||||
_gnk = _tpl_def.get("guest_name_key", "")
|
_gnk = _tpl_def.get("guest_name_key", "")
|
||||||
if _gnk:
|
if _gnk:
|
||||||
params[_gnk] = guest.first_name or guest_name
|
params[_gnk] = guest.first_name or guest_name
|
||||||
|
|||||||
@ -353,3 +353,31 @@ CREATE TABLE IF NOT EXISTS rsvp_tokens (
|
|||||||
);
|
);
|
||||||
CREATE INDEX IF NOT EXISTS idx_rsvp_tokens_event_id ON rsvp_tokens(event_id);
|
CREATE INDEX IF NOT EXISTS idx_rsvp_tokens_event_id ON rsvp_tokens(event_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_rsvp_tokens_guest_id ON rsvp_tokens(guest_id);
|
CREATE INDEX IF NOT EXISTS idx_rsvp_tokens_guest_id ON rsvp_tokens(guest_id);
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- STEP 16: WhatsApp Templates Table
|
||||||
|
-- Store custom WhatsApp message templates created by users
|
||||||
|
-- ============================================
|
||||||
|
CREATE TABLE IF NOT EXISTS whatsapp_templates (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
template_key TEXT NOT NULL UNIQUE,
|
||||||
|
meta_name TEXT NOT NULL,
|
||||||
|
friendly_name TEXT NOT NULL,
|
||||||
|
language_code TEXT DEFAULT 'he' NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
header_text TEXT,
|
||||||
|
body_text TEXT,
|
||||||
|
header_params TEXT DEFAULT '[]' NOT NULL,
|
||||||
|
body_params TEXT DEFAULT '[]' NOT NULL,
|
||||||
|
fallbacks TEXT DEFAULT '{}' NOT NULL,
|
||||||
|
button_type TEXT,
|
||||||
|
button_text TEXT,
|
||||||
|
button_url TEXT,
|
||||||
|
header_type TEXT DEFAULT 'TEXT' NOT NULL,
|
||||||
|
header_handle TEXT,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_whatsapp_templates_key ON whatsapp_templates(template_key);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_whatsapp_templates_created ON whatsapp_templates(created_at);
|
||||||
|
|||||||
@ -136,3 +136,41 @@ class RsvpToken(Base):
|
|||||||
|
|
||||||
event = relationship("Event")
|
event = relationship("Event")
|
||||||
guest = relationship("Guest")
|
guest = relationship("Guest")
|
||||||
|
|
||||||
|
|
||||||
|
# ── WhatsApp Custom Templates ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class WhatsAppTemplate(Base):
|
||||||
|
"""
|
||||||
|
Stores custom WhatsApp message templates created by users.
|
||||||
|
Built-in templates are defined in code, but custom templates are persisted here.
|
||||||
|
"""
|
||||||
|
__tablename__ = "whatsapp_templates"
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||||
|
template_key = Column(String, unique=True, nullable=False, index=True) # e.g., "wedding_invitation_custom_1"
|
||||||
|
meta_name = Column(String, nullable=False) # Name as it appears in Meta Business Manager
|
||||||
|
friendly_name = Column(String, nullable=False) # Display name in frontend
|
||||||
|
language_code = Column(String, default="he", nullable=False) # e.g., "he", "en"
|
||||||
|
description = Column(Text, nullable=True)
|
||||||
|
|
||||||
|
# Template structure
|
||||||
|
header_text = Column(Text, nullable=True) # Header content
|
||||||
|
body_text = Column(Text, nullable=True) # Body content with {{1}}, {{2}} placeholders
|
||||||
|
|
||||||
|
# Parameters (stored as JSON string)
|
||||||
|
header_params = Column(Text, nullable=False, default="[]") # JSON array
|
||||||
|
body_params = Column(Text, nullable=False, default="[]") # JSON array
|
||||||
|
fallbacks = Column(Text, nullable=False, default="{}") # JSON object with default values
|
||||||
|
|
||||||
|
# Optional: Button configuration
|
||||||
|
button_type = Column(String, nullable=True) # "URL", "PHONE_NUMBER", "QUICK_REPLY"
|
||||||
|
button_text = Column(String, nullable=True)
|
||||||
|
button_url = Column(String, nullable=True)
|
||||||
|
|
||||||
|
# Media support
|
||||||
|
header_type = Column(String, default="TEXT", nullable=False) # "TEXT", "IMAGE", "VIDEO", "DOCUMENT"
|
||||||
|
header_handle = Column(String, nullable=True) # Media handle for IMAGE/VIDEO/DOCUMENT
|
||||||
|
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||||
|
|||||||
@ -23,11 +23,12 @@ class WhatsAppError(Exception):
|
|||||||
class WhatsAppService:
|
class WhatsAppService:
|
||||||
"""Service for sending WhatsApp messages via Meta API"""
|
"""Service for sending WhatsApp messages via Meta API"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, db=None):
|
||||||
self.access_token = os.getenv("WHATSAPP_ACCESS_TOKEN")
|
self.access_token = os.getenv("WHATSAPP_ACCESS_TOKEN")
|
||||||
self.phone_number_id = os.getenv("WHATSAPP_PHONE_NUMBER_ID")
|
self.phone_number_id = os.getenv("WHATSAPP_PHONE_NUMBER_ID")
|
||||||
self.api_version = os.getenv("WHATSAPP_API_VERSION", "v20.0")
|
self.api_version = os.getenv("WHATSAPP_API_VERSION", "v20.0")
|
||||||
self.verify_token = os.getenv("WHATSAPP_VERIFY_TOKEN", "")
|
self.verify_token = os.getenv("WHATSAPP_VERIFY_TOKEN", "")
|
||||||
|
self.db = db # Database session for template lookups
|
||||||
|
|
||||||
if not self.access_token or not self.phone_number_id:
|
if not self.access_token or not self.phone_number_id:
|
||||||
raise WhatsAppError(
|
raise WhatsAppError(
|
||||||
@ -438,7 +439,7 @@ class WhatsAppService:
|
|||||||
"""
|
"""
|
||||||
from whatsapp_templates import get_template, build_params_list
|
from whatsapp_templates import get_template, build_params_list
|
||||||
|
|
||||||
tpl = get_template(template_key)
|
tpl = get_template(self.db, template_key)
|
||||||
meta_name = tpl["meta_name"]
|
meta_name = tpl["meta_name"]
|
||||||
language_code = tpl.get("language_code", "he")
|
language_code = tpl.get("language_code", "he")
|
||||||
header_type = tpl.get("header_type", "TEXT")
|
header_type = tpl.get("header_type", "TEXT")
|
||||||
@ -446,7 +447,7 @@ class WhatsAppService:
|
|||||||
button_type = tpl.get("button_type", "")
|
button_type = tpl.get("button_type", "")
|
||||||
button_url = tpl.get("button_url", "")
|
button_url = tpl.get("button_url", "")
|
||||||
|
|
||||||
header_values, body_values = build_params_list(template_key, params)
|
header_values, body_values = build_params_list(self.db, template_key, params)
|
||||||
|
|
||||||
to_e164 = self.normalize_phone_to_e164(to_phone)
|
to_e164 = self.normalize_phone_to_e164(to_phone)
|
||||||
if not self.validate_phone(to_e164):
|
if not self.validate_phone(to_e164):
|
||||||
@ -633,9 +634,12 @@ class WhatsAppService:
|
|||||||
_whatsapp_service: Optional[WhatsAppService] = None
|
_whatsapp_service: Optional[WhatsAppService] = None
|
||||||
|
|
||||||
|
|
||||||
def get_whatsapp_service() -> WhatsAppService:
|
def get_whatsapp_service(db=None) -> WhatsAppService:
|
||||||
"""Get or create WhatsApp service singleton"""
|
"""Get or create WhatsApp service singleton. Pass db if you need template lookups."""
|
||||||
global _whatsapp_service
|
global _whatsapp_service
|
||||||
if _whatsapp_service is None:
|
if _whatsapp_service is None:
|
||||||
_whatsapp_service = WhatsAppService()
|
_whatsapp_service = WhatsAppService(db=db)
|
||||||
|
# Update db if provided (for template lookups)
|
||||||
|
if db is not None:
|
||||||
|
_whatsapp_service.db = db
|
||||||
return _whatsapp_service
|
return _whatsapp_service
|
||||||
|
|||||||
@ -33,53 +33,102 @@ IMPORTANT: param order in header_params / body_params MUST match the
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any, Optional
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
# ── Custom templates file ─────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
CUSTOM_TEMPLATES_FILE = os.path.join(os.path.dirname(__file__), "custom_templates.json")
|
|
||||||
|
|
||||||
|
|
||||||
def load_custom_templates() -> Dict[str, Dict[str, Any]]:
|
def load_custom_templates(db: Session) -> Dict[str, Dict[str, Any]]:
|
||||||
"""Load user-created templates from the JSON store."""
|
"""Load user-created templates from the database."""
|
||||||
|
from models import WhatsAppTemplate
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(CUSTOM_TEMPLATES_FILE, "r", encoding="utf-8") as f:
|
templates = db.query(WhatsAppTemplate).all()
|
||||||
return json.load(f)
|
result = {}
|
||||||
except (FileNotFoundError, json.JSONDecodeError):
|
for t in templates:
|
||||||
|
result[t.template_key] = {
|
||||||
|
"meta_name": t.meta_name,
|
||||||
|
"language_code": t.language_code,
|
||||||
|
"friendly_name": t.friendly_name,
|
||||||
|
"description": t.description,
|
||||||
|
"header_type": t.header_type,
|
||||||
|
"header_text": t.header_text,
|
||||||
|
"header_handle": t.header_handle,
|
||||||
|
"body_text": t.body_text,
|
||||||
|
"header_params": json.loads(t.header_params),
|
||||||
|
"body_params": json.loads(t.body_params),
|
||||||
|
"button_type": t.button_type,
|
||||||
|
"button_text": t.button_text,
|
||||||
|
"button_url": t.button_url,
|
||||||
|
"button_param_key": t.button_url, # backward compat
|
||||||
|
"fallbacks": json.loads(t.fallbacks),
|
||||||
|
"guest_name_key": "",
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WARNING] Failed to load custom templates from database: {e}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def save_custom_templates(data: Dict[str, Dict[str, Any]]) -> None:
|
def save_custom_templates(db: Session, data: Dict[str, Dict[str, Any]]) -> None:
|
||||||
"""Persist custom templates to the JSON store."""
|
"""Persist custom templates to the database."""
|
||||||
with open(CUSTOM_TEMPLATES_FILE, "w", encoding="utf-8") as f:
|
from models import WhatsAppTemplate
|
||||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
||||||
|
try:
|
||||||
|
# Clear old templates
|
||||||
|
db.query(WhatsAppTemplate).delete()
|
||||||
|
|
||||||
|
# Add new ones
|
||||||
|
for key, tpl in data.items():
|
||||||
|
template_record = WhatsAppTemplate(
|
||||||
|
template_key=key,
|
||||||
|
meta_name=tpl.get("meta_name", key),
|
||||||
|
friendly_name=tpl.get("friendly_name", key),
|
||||||
|
language_code=tpl.get("language_code", "he"),
|
||||||
|
description=tpl.get("description", ""),
|
||||||
|
header_type=tpl.get("header_type", "TEXT"),
|
||||||
|
header_text=tpl.get("header_text", ""),
|
||||||
|
header_handle=tpl.get("header_handle", ""),
|
||||||
|
body_text=tpl.get("body_text", ""),
|
||||||
|
header_params=json.dumps(tpl.get("header_params", [])),
|
||||||
|
body_params=json.dumps(tpl.get("body_params", [])),
|
||||||
|
button_type=tpl.get("button_type", ""),
|
||||||
|
button_text=tpl.get("button_text", ""),
|
||||||
|
button_url=tpl.get("button_url", ""),
|
||||||
|
fallbacks=json.dumps(tpl.get("fallbacks", {})),
|
||||||
|
)
|
||||||
|
db.add(template_record)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WARNING] Failed to save custom templates to database: {e}")
|
||||||
|
db.rollback()
|
||||||
|
|
||||||
|
|
||||||
def get_all_templates() -> Dict[str, Dict[str, Any]]:
|
def get_all_templates(db: Session) -> Dict[str, Dict[str, Any]]:
|
||||||
"""Return merged dict: built-in TEMPLATES + user custom templates."""
|
"""Return merged dict: built-in TEMPLATES + user custom templates."""
|
||||||
merged = dict(TEMPLATES)
|
merged = dict(TEMPLATES)
|
||||||
merged.update(load_custom_templates())
|
merged.update(load_custom_templates(db))
|
||||||
return merged
|
return merged
|
||||||
|
|
||||||
|
|
||||||
def add_custom_template(key: str, template: Dict[str, Any]) -> None:
|
def add_custom_template(db: Session, key: str, template: Dict[str, Any]) -> None:
|
||||||
"""Add or overwrite a custom template (cannot replace built-ins)."""
|
"""Add or overwrite a custom template (cannot replace built-ins)."""
|
||||||
if key in TEMPLATES:
|
if key in TEMPLATES:
|
||||||
raise ValueError(f"Template key '{key}' is a built-in and cannot be overwritten.")
|
raise ValueError(f"Template key '{key}' is a built-in and cannot be overwritten.")
|
||||||
data = load_custom_templates()
|
data = load_custom_templates(db)
|
||||||
data[key] = template
|
data[key] = template
|
||||||
save_custom_templates(data)
|
save_custom_templates(db, data)
|
||||||
|
|
||||||
|
|
||||||
def delete_custom_template(key: str) -> None:
|
def delete_custom_template(db: Session, key: str) -> None:
|
||||||
"""Delete a custom template by key. Raises KeyError if not found."""
|
"""Delete a custom template by key. Raises KeyError if not found."""
|
||||||
if key in TEMPLATES:
|
if key in TEMPLATES:
|
||||||
raise ValueError(f"Template key '{key}' is a built-in and cannot be deleted.")
|
raise ValueError(f"Template key '{key}' is a built-in and cannot be deleted.")
|
||||||
data = load_custom_templates()
|
data = load_custom_templates(db)
|
||||||
if key not in data:
|
if key not in data:
|
||||||
raise KeyError(f"Custom template '{key}' not found.")
|
raise KeyError(f"Custom template '{key}' not found.")
|
||||||
del data[key]
|
del data[key]
|
||||||
save_custom_templates(data)
|
save_custom_templates(db, data)
|
||||||
|
|
||||||
|
|
||||||
# ── Template registry ─────────────────────────────────────────────────────────
|
# ── Template registry ─────────────────────────────────────────────────────────
|
||||||
@ -187,12 +236,12 @@ TEMPLATES: Dict[str, Dict[str, Any]] = {
|
|||||||
|
|
||||||
# ── Helper functions ──────────────────────────────────────────────────────────
|
# ── Helper functions ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def get_template(key: str) -> Dict[str, Any]:
|
def get_template(db: Session, key: str) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Return the template definition for *key* (checks both built-in + custom).
|
Return the template definition for *key* (checks both built-in + custom).
|
||||||
Raises KeyError with a helpful message if not found.
|
Raises KeyError with a helpful message if not found.
|
||||||
"""
|
"""
|
||||||
all_tpls = get_all_templates()
|
all_tpls = get_all_templates(db)
|
||||||
if key not in all_tpls:
|
if key not in all_tpls:
|
||||||
available = ", ".join(all_tpls.keys())
|
available = ", ".join(all_tpls.keys())
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
@ -202,13 +251,13 @@ def get_template(key: str) -> Dict[str, Any]:
|
|||||||
return all_tpls[key]
|
return all_tpls[key]
|
||||||
|
|
||||||
|
|
||||||
def list_templates_for_frontend() -> list:
|
def list_templates_for_frontend(db: Session) -> list:
|
||||||
"""
|
"""
|
||||||
Return a list suitable for the frontend dropdown (built-in + custom).
|
Return a list suitable for the frontend dropdown (built-in + custom).
|
||||||
Each item: {key, friendly_name, meta_name, param_count, language_code, description, is_custom}
|
Each item: {key, friendly_name, meta_name, param_count, language_code, description, is_custom}
|
||||||
"""
|
"""
|
||||||
all_tpls = get_all_templates()
|
all_tpls = get_all_templates(db)
|
||||||
custom_keys = set(load_custom_templates().keys())
|
custom_keys = set(load_custom_templates(db).keys())
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"key": key,
|
"key": key,
|
||||||
@ -237,14 +286,14 @@ def list_templates_for_frontend() -> list:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def build_params_list(key: str, values: dict) -> tuple:
|
def build_params_list(db: Session, key: str, values: dict) -> tuple:
|
||||||
"""
|
"""
|
||||||
Given a template key and a dict of {param_key: value}, return
|
Given a template key and a dict of {param_key: value}, return
|
||||||
(header_params_list, body_params_list) after applying fallbacks.
|
(header_params_list, body_params_list) after applying fallbacks.
|
||||||
|
|
||||||
Both lists contain plain string values in correct order.
|
Both lists contain plain string values in correct order.
|
||||||
"""
|
"""
|
||||||
tpl = get_template(key) # checks built-in + custom
|
tpl = get_template(db, key) # checks built-in + custom
|
||||||
fallbacks = tpl.get("fallbacks", {})
|
fallbacks = tpl.get("fallbacks", {})
|
||||||
|
|
||||||
def resolve(param_key: str) -> str:
|
def resolve(param_key: str) -> str:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user