feat(chalice): msteams add

feat(chalice): msteams update
feat(chalice): msteams share session
feat(chalice): msteams share error
This commit is contained in:
Taha Yassine Kraiem 2022-11-17 19:28:45 +01:00
parent e176f9bd44
commit 30eca5e8fb
7 changed files with 129 additions and 75 deletions

View file

@ -30,7 +30,7 @@ class BaseCollaboration(ABC):
@classmethod
@abstractmethod
def __share(cls, tenant_id, integration_id, fallback, pretext, title, title_link, text):
def __share(cls, tenant_id, integration_id, attachments):
pass
@classmethod

View file

@ -1,6 +1,5 @@
import requests
from decouple import config
from datetime import datetime
import schemas
from chalicelib.core import webhook
@ -16,6 +15,7 @@ class MSTeams(BaseCollaboration):
webhook_type="msteams",
name=data.name)
return None
# https://messagecardplayground.azurewebsites.net
@classmethod
def say_hello(cls, url):
@ -58,6 +58,7 @@ class MSTeams(BaseCollaboration):
@classmethod
def send_batch(cls, tenant_id, webhook_id, attachments):
# TODO: change this
integration = cls.__get(tenant_id=tenant_id, integration_id=webhook_id)
if integration is None:
return {"errors": ["slack integration not found"]}
@ -72,50 +73,87 @@ class MSTeams(BaseCollaboration):
print(r.text)
@classmethod
def __share(cls, tenant_id, integration_id, fallback, pretext, title, title_link, text):
def __share(cls, tenant_id, integration_id, attachement):
integration = cls.__get(tenant_id=tenant_id, integration_id=integration_id)
if integration is None:
return {"errors": ["slack integration not found"]}
return {"errors": ["Microsoft Teams integration not found"]}
r = requests.post(
url=integration["endpoint"],
json={
"attachments": [
{
"fallback": fallback,
"pretext": pretext,
"title": title,
"title_link": title_link,
"text": text,
"ts": datetime.now().timestamp()
}
]
})
json={"type": "message",
"attachments": [
{"contentType": "application/vnd.microsoft.card.adaptive",
"contentUrl": None,
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.2",
"body": [attachement]}}
]
})
return r.text
@classmethod
def share_session(cls, tenant_id, project_id, session_id, user, comment, integration_id=None):
args = {"fallback": f"{user} has shared the below session!",
"pretext": f"{user} has shared the below session!",
"title": f"{config('SITE_URL')}/{project_id}/session/{session_id}",
"title_link": f"{config('SITE_URL')}/{project_id}/session/{session_id}",
"text": comment}
return {"data": cls.__share(tenant_id, integration_id, **args)}
title = f"[{user}](mailto:{user}) has shared the below session!"
link = f"{config('SITE_URL')}/{project_id}/session/{session_id}"
link = f"[{link}]({link})"
args = {"type": "ColumnSet",
"columns": [{
"width": "stretch",
"items": [
{"type": "TextBlock",
"text": title},
{"type": "TextBlock",
"spacing": "small",
"text": link}
]
}]}
if comment and len(comment) > 0:
args["columns"][0]["items"].append({
"type": "TextBlock",
"spacing": "small",
"text": comment
})
data = cls.__share(tenant_id, integration_id, attachement=args)
if "errors" in data:
return data
return {"data": data}
@classmethod
def share_error(cls, tenant_id, project_id, error_id, user, comment, integration_id=None):
args = {"fallback": f"{user} has shared the below error!",
"pretext": f"{user} has shared the below error!",
"title": f"{config('SITE_URL')}/{project_id}/errors/{error_id}",
"title_link": f"{config('SITE_URL')}/{project_id}/errors/{error_id}",
"text": comment}
return {"data": cls.__share(tenant_id, integration_id, **args)}
title = f"[{user}](mailto:{user}) has shared the below error!"
link = f"{config('SITE_URL')}/{project_id}/errors/{error_id}"
link = f"[{link}]({link})"
args = {"type": "ColumnSet",
"columns": [{
"width": "stretch",
"items": [
{"type": "TextBlock",
"text": title},
{"type": "TextBlock",
"spacing": "small",
"text": link}
]
}]}
if comment and len(comment) > 0:
args["columns"][0]["items"].append({
"type": "TextBlock",
"spacing": "small",
"text": comment
})
data = cls.__share(tenant_id, integration_id, attachement=args)
if "errors" in data:
return data
return {"data": data}
@classmethod
def __get(cls, tenant_id, integration_id=None):
if integration_id is not None:
return webhook.get(tenant_id=tenant_id, webhook_id=integration_id)
return webhook.get_webhook(tenant_id=tenant_id, webhook_id=integration_id,
webhook_type=schemas.WebhookType.msteams)
integrations = webhook.get_by_type(tenant_id=tenant_id, webhook_type="slack")
integrations = webhook.get_by_type(tenant_id=tenant_id, webhook_type=schemas.WebhookType.msteams)
if integrations is None or len(integrations) == 0:
return None
return integrations[0]

View file

@ -75,24 +75,12 @@ class Slack(BaseCollaboration):
print(r.text)
@classmethod
def __share(cls, tenant_id, integration_id, fallback, pretext, title, title_link, text):
def __share(cls, tenant_id, integration_id, attachement):
integration = cls.__get(tenant_id=tenant_id, integration_id=integration_id)
if integration is None:
return {"errors": ["slack integration not found"]}
r = requests.post(
url=integration["endpoint"],
json={
"attachments": [
{
"fallback": fallback,
"pretext": pretext,
"title": title,
"title_link": title_link,
"text": text,
"ts": datetime.now().timestamp()
}
]
})
attachement["ts"] = datetime.now().timestamp()
r = requests.post(url=integration["endpoint"], json={"attachments": [attachement]})
return r.text
@classmethod
@ -102,7 +90,10 @@ class Slack(BaseCollaboration):
"title": f"{config('SITE_URL')}/{project_id}/session/{session_id}",
"title_link": f"{config('SITE_URL')}/{project_id}/session/{session_id}",
"text": comment}
return {"data": cls.__share(tenant_id, integration_id, **args)}
data = cls.__share(tenant_id, integration_id, attachement=args)
if "errors" in data:
return data
return {"data": data}
@classmethod
def share_error(cls, tenant_id, project_id, error_id, user, comment, integration_id=None):
@ -111,14 +102,18 @@ class Slack(BaseCollaboration):
"title": f"{config('SITE_URL')}/{project_id}/errors/{error_id}",
"title_link": f"{config('SITE_URL')}/{project_id}/errors/{error_id}",
"text": comment}
return {"data": cls.__share(tenant_id, integration_id, **args)}
data = cls.__share(tenant_id, integration_id, attachement=args)
if "errors" in data:
return data
return {"data": data}
@classmethod
def __get(cls, tenant_id, integration_id=None):
if integration_id is not None:
return webhook.get(tenant_id=tenant_id, webhook_id=integration_id)
return webhook.get_webhook(tenant_id=tenant_id, webhook_id=integration_id,
webhook_type=schemas.WebhookType.slack)
integrations = webhook.get_by_type(tenant_id=tenant_id, webhook_type="slack")
integrations = webhook.get_by_type(tenant_id=tenant_id, webhook_type=schemas.WebhookType.slack)
if integrations is None or len(integrations) == 0:
return None
return integrations[0]

View file

@ -21,14 +21,14 @@ def get_by_id(webhook_id):
return w
def get(tenant_id, webhook_id):
def get_webhook(tenant_id, webhook_id, webhook_type='webhook'):
with pg_client.PostgresClient() as cur:
cur.execute(
cur.mogrify("""SELECT w.*
FROM public.webhooks AS w
WHERE w.webhook_id =%(webhook_id)s
AND deleted_at ISNULL AND type='webhook';""",
{"webhook_id": webhook_id})
AND deleted_at ISNULL AND type=%(webhook_type)s;""",
{"webhook_id": webhook_id, "webhook_type": webhook_type})
)
w = helper.dict_to_camel_case(cur.fetchone())
if w:
@ -70,7 +70,7 @@ def update(tenant_id, webhook_id, changes, replace_none=False):
UPDATE public.webhooks
SET {','.join(sub_query)}
WHERE webhook_id =%(id)s AND deleted_at ISNULL
RETURNING webhook_id AS integration_id, webhook_id AS id,*;""",
RETURNING *;""",
{"id": webhook_id, **changes})
)
w = helper.dict_to_camel_case(cur.fetchone())
@ -87,7 +87,7 @@ def add(tenant_id, endpoint, auth_header=None, webhook_type='webhook', name="",
query = cur.mogrify("""\
INSERT INTO public.webhooks(endpoint,auth_header,type,name)
VALUES (%(endpoint)s, %(auth_header)s, %(type)s,%(name)s)
RETURNING webhook_id AS integration_id, webhook_id AS id,*;""",
RETURNING *;""",
{"endpoint": endpoint, "auth_header": auth_header,
"type": webhook_type, "name": name})
cur.execute(

View file

@ -97,21 +97,27 @@ def get_integrations_status(projectId: int, context: schemas.CurrentContext = De
return {"data": data}
@app.post('/{projectId}/integrations/{integration}/notify/{integrationId}/{source}/{sourceId}', tags=["integrations"])
def integration_notify(projectId: int, integration: str, integrationId: int, source: str, sourceId: str,
@app.post('/{projectId}/integrations/{integration}/notify/{webhookId}/{source}/{sourceId}', tags=["integrations"])
def integration_notify(projectId: int, integration: str, webhookId: int, source: str, sourceId: str,
data: schemas.IntegrationNotificationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
comment = None
if data.comment:
comment = data.comment
if integration == "slack":
args = {"tenant_id": context.tenant_id,
"user": context.email, "comment": comment, "project_id": projectId,
"integration_id": integrationId}
args = {"tenant_id": context.tenant_id,
"user": context.email, "comment": comment, "project_id": projectId,
"integration_id": webhookId}
if integration == schemas.WebhookType.slack:
if source == "sessions":
return Slack.share_session(session_id=sourceId, **args)
elif source == "errors":
return Slack.share_error(error_id=sourceId, **args)
elif integration == schemas.WebhookType.msteams:
if source == "sessions":
return MSTeams.share_session(session_id=sourceId, **args)
elif source == "errors":
return Slack.share_error(error_id=sourceId, **args)
return {"data": None}
@ -856,17 +862,18 @@ def get_boarding_state_integrations(context: schemas.CurrentContext = Depends(OR
@app.get('/integrations/slack/channels', tags=["integrations"])
def get_slack_channels(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.get_by_type(tenant_id=context.tenant_id, webhook_type='slack')}
return {"data": webhook.get_by_type(tenant_id=context.tenant_id, webhook_type=schemas.WebhookType.slack)}
@app.get('/integrations/slack/{integrationId}', tags=["integrations"])
def get_slack_webhook(integrationId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.get(tenant_id=context.tenant_id, webhook_id=integrationId)}
@app.get('/integrations/slack/{webhookId}', tags=["integrations"])
def get_slack_webhook(webhookId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.get_webhook(tenant_id=context.tenant_id, webhook_id=webhookId,
webhook_type=schemas.WebhookType.slack)}
@app.delete('/integrations/slack/{integrationId}', tags=["integrations"])
def delete_slack_integration(integrationId: int, context: schemas.CurrentContext = Depends(OR_context)):
return webhook.delete(context.tenant_id, integrationId)
@app.delete('/integrations/slack/{webhookId}', tags=["integrations"])
def delete_slack_integration(webhookId: int, context: schemas.CurrentContext = Depends(OR_context)):
return webhook.delete(context.tenant_id, webhookId)
@app.put('/webhooks', tags=["webhooks"])
@ -950,29 +957,36 @@ def get_limits(context: schemas.CurrentContext = Depends(OR_context)):
}
@app.get('/integrations/msteams/channels', tags=["integrations"])
def get_msteams_channels(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.get_by_type(tenant_id=context.tenant_id, webhook_type=schemas.WebhookType.msteams)}
@app.post('/integrations/msteams', tags=['integrations'])
def add_msteams_integration(data: schemas.AddCollaborationSchema,
context: schemas.CurrentContext = Depends(OR_context)):
n = MSTeams.add(tenant_id=context.tenant_id, data=data)
if n is None:
return {
"errors": ["We couldn't send you a test message on your Microsoft Teams channel. Please verify your webhook url."]
"errors": [
"We couldn't send you a test message on your Microsoft Teams channel. Please verify your webhook url."]
}
return {"data": n}
@app.post('/integrations/msteams/{integrationId}', tags=['integrations'])
def edit_msteams_integration(integrationId: int, data: schemas.EditCollaborationSchema = Body(...),
@app.post('/integrations/msteams/{webhookId}', tags=['integrations'])
def edit_msteams_integration(webhookId: int, data: schemas.EditCollaborationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if len(data.url) > 0:
old = webhook.get(tenant_id=context.tenant_id, webhook_id=integrationId)
old = webhook.get_webhook(tenant_id=context.tenant_id, webhook_id=webhookId,
webhook_type=schemas.WebhookType.msteams)
if old["endpoint"] != data.url:
if not Slack.say_hello(data.url):
if not MSTeams.say_hello(data.url):
return {
"errors": [
"We couldn't send you a test message on your Slack channel. Please verify your webhook url."]
"We couldn't send you a test message on your Microsoft Teams channel. Please verify your webhook url."]
}
return {"data": webhook.update(tenant_id=context.tenant_id, webhook_id=integrationId,
return {"data": webhook.update(tenant_id=context.tenant_id, webhook_id=webhookId,
changes={"name": data.name, "endpoint": data.url})}

View file

@ -84,7 +84,7 @@ def add_slack_integration(data: schemas.AddCollaborationSchema, context: schemas
def edit_slack_integration(integrationId: int, data: schemas.EditCollaborationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if len(data.url) > 0:
old = webhook.get(tenant_id=context.tenant_id, webhook_id=integrationId)
old = webhook.get_webhook(tenant_id=context.tenant_id, webhook_id=integrationId)
if old["endpoint"] != data.url:
if not Slack.say_hello(data.url):
return {

View file

@ -1120,3 +1120,10 @@ class SessionUpdateNoteSchema(SessionNoteSchema):
break
assert c > 0, "at least 1 value should be provided for update"
return values
class WebhookType(str, Enum):
webhook = "webhook"
slack = "slack"
email = "email"
msteams = "msteams"