• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

digiteinfotech / kairon / 12112350604

02 Dec 2024 03:52AM UTC coverage: 89.891% (-0.04%) from 89.932%
12112350604

Pull #1611

github

web-flow
Merge 9176d03d1 into f2f296b80
Pull Request #1611: Mail channel implementation

383 of 434 new or added lines in 15 files covered. (88.25%)

12 existing lines in 2 files now uncovered.

24141 of 26856 relevant lines covered (89.89%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/kairon/events/server.py
1
from typing import Text
1✔
2

3
from fastapi import FastAPI, Request, Path, Query
1✔
4
from fastapi.middleware.cors import CORSMiddleware
1✔
5
from fastapi.middleware.gzip import GZipMiddleware
1✔
6
from fastapi.responses import JSONResponse
1✔
7
from loguru import logger
1✔
8
from mongoengine import connect, disconnect
1✔
9
from secure import StrictTransportSecurity, ReferrerPolicy, ContentSecurityPolicy, XContentTypeOptions, Server, \
1✔
10
    CacheControl, Secure, PermissionsPolicy
11

12
from kairon.api.models import Response
1✔
13
from kairon.events.models import EventRequest
1✔
14
from kairon.events.scheduler.kscheduler import KScheduler
1✔
15
from kairon.events.utility import EventUtility
1✔
16
from kairon.shared.constants import EventClass
1✔
17
from kairon.shared.utils import Utility
1✔
18
from contextlib import asynccontextmanager
1✔
19
from kairon.shared.otel import instrument_fastapi
1✔
20

21

22
hsts = StrictTransportSecurity().include_subdomains().preload().max_age(31536000)
1✔
23
referrer = ReferrerPolicy().no_referrer()
1✔
24
csp = (
1✔
25
    ContentSecurityPolicy()
26
    .default_src("'self'")
27
    .frame_ancestors("'self'")
28
    .form_action("'self'")
29
    .base_uri("'self'")
30
    .connect_src("'self'")
31
    .frame_src("'self'")
32
    .style_src("'self'", "https:", "'unsafe-inline'")
33
    .img_src("'self'", "https:")
34
    .script_src("'self'", "https:", "'unsafe-inline'")
35
)
36
cache_value = CacheControl().must_revalidate()
1✔
37
content = XContentTypeOptions()
1✔
38
server = Server().set("Secure")
1✔
39
permissions_value = (
1✔
40
    PermissionsPolicy().accelerometer().autoplay().camera().document_domain().encrypted_media().fullscreen().vibrate()
41
    .geolocation().gyroscope().magnetometer().microphone().midi().payment().picture_in_picture().sync_xhr().usb()
42
)
43
secure_headers = Secure(
1✔
44
    server=server,
45
    csp=csp,
46
    hsts=hsts,
47
    referrer=referrer,
48
    permissions=permissions_value,
49
    cache=cache_value,
50
    content=content
51
)
52

53

54
@asynccontextmanager
1✔
55
async def lifespan(app: FastAPI):
1✔
56
    """ MongoDB is connected on the bot trainer startup """
57
    config: dict = Utility.mongoengine_connection(Utility.environment['database']["url"])
×
58
    connect(**config)
×
NEW
59
    from kairon.shared.channels.mail.scheduler import MailScheduler
×
NEW
60
    MailScheduler.epoch()
×
61
    yield
×
62
    disconnect()
×
63

64

65
app = FastAPI(lifespan=lifespan)
1✔
66
allowed_origins = Utility.environment['cors']['origin']
1✔
67
app.add_middleware(
1✔
68
    CORSMiddleware,
69
    allow_origins=allowed_origins,
70
    allow_credentials=True,
71
    allow_methods=["*"],
72
    allow_headers=["*"],
73
    expose_headers=["content-disposition"],
74
)
75
app.add_middleware(GZipMiddleware)
1✔
76
instrument_fastapi(app)
1✔
77

78

79
@app.middleware("http")
1✔
80
async def add_secure_headers(request: Request, call_next):
1✔
81
    """add security headers"""
82
    response = await call_next(request)
1✔
83
    secure_headers.framework.fastapi(response)
1✔
84
    response.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'
1✔
85
    response.headers['Cross-Origin-Opener-Policy'] = 'same-origin'
1✔
86
    response.headers['Cross-Origin-Resource-Policy'] = 'same-origin'
1✔
87
    requested_origin = request.headers.get("origin")
1✔
88
    response.headers["Access-Control-Allow-Origin"] = requested_origin if requested_origin else allowed_origins[0]
1✔
89
    response.headers['Cross-Origin-Resource-Policy'] = 'same-origin'
1✔
90
    response.headers['Content-Type'] = 'application/json'
1✔
91
    return response
1✔
92

93

94
@app.middleware("http")
1✔
95
async def catch_exceptions_middleware(request: Request, call_next):
1✔
96
    try:
1✔
97
        return await call_next(request)
1✔
98
    except Exception as exc:
1✔
99
        logger.exception(exc)
1✔
100

101
        return JSONResponse(
1✔
102
            Response(
103
                success=False, error_code=422, message=str(exc)
104
            ).dict()
105
        )
106

107

108
@app.get("/", response_model=Response)
1✔
109
def index():
1✔
110
    return {"message": "Event server running!"}
1✔
111

112

113
@app.get("/healthcheck", response_model=Response)
1✔
114
def healthcheck():
1✔
115
    return {"message": "health check ok"}
1✔
116

117

118
@app.post("/api/events/execute/{event_type}", response_model=Response)
1✔
119
def add_event(
1✔
120
        request: EventRequest,
121
        is_scheduled: bool = Query(default=False, description="Whether the event is to be run once or scheduled"),
122
        event_type: EventClass = Path(description="Event type", examples=[e.value for e in EventClass])
123
):
124
    request.validate_request(is_scheduled, event_type)
1✔
125
    response, message = EventUtility.add_job(event_type, request.dict(), is_scheduled)
1✔
126
    return {"data": response, "message": message}
1✔
127

128

129
@app.put("/api/events/execute/{event_type}", response_model=Response)
1✔
130
def update_scheduled_event(
1✔
131
        request: EventRequest,
132
        is_scheduled: bool = Query(default=False, description="Whether the event is to be run once or scheduled"),
133
        event_type: EventClass = Path(description="Event type", examples=[e.value for e in EventClass])
134
):
135
    request.validate_request(is_scheduled, event_type)
1✔
136
    response, message = EventUtility.update_job(event_type, request.dict(), is_scheduled)
1✔
137
    return {"data": response, "message": message}
1✔
138

139

140
@app.delete("/api/events/{event_id}", response_model=Response)
1✔
141
def delete_scheduled_event(event_id: Text = Path(description="Event id")):
1✔
142
    return {"data": KScheduler().delete_job(event_id), "message": "Scheduled event deleted!"}
1✔
143

144

145
@app.get("/api/events/dispatch/{event_id}", response_model=Response)
1✔
146
def dispatch_scheduled_event(event_id: Text = Path(description="Event id")):
1✔
147
    KScheduler().dispatch_event(event_id)
1✔
148
    return {"data": None, "message": "Scheduled event dispatch!"}
1✔
149

150

151
@app.get('/api/mail/request_epoch', response_model=Response)
1✔
152
def request_epoch():
1✔
153
    from kairon.shared.channels.mail.scheduler import MailScheduler
1✔
154
    MailScheduler.epoch()
1✔
155
    return {"data": None, "message": "Mail scheduler epoch request!"}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc