Skip to content
Draft

wip #3325

Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
wip
  • Loading branch information
StanGirard committed Oct 5, 2024
commit 69a2bb576d5582f044241abf64a7b45298833015
8 changes: 8 additions & 0 deletions backend/api/quivr_api/modules/sync/controller/sync_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from quivr_api.modules.sync.controller.github_sync_routes import github_sync_router
from quivr_api.modules.sync.controller.google_sync_routes import google_sync_router
from quivr_api.modules.sync.controller.notion_sync_routes import notion_sync_router
from quivr_api.modules.sync.controller.zendesk_sync_routes import zendesk_sync_router
from quivr_api.modules.sync.dto import SyncsDescription
from quivr_api.modules.sync.dto.inputs import SyncsActiveInput, SyncsActiveUpdateInput
from quivr_api.modules.sync.dto.outputs import AuthMethodEnum
Expand Down Expand Up @@ -49,6 +50,7 @@
sync_router.include_router(github_sync_router)
sync_router.include_router(dropbox_sync_router)
sync_router.include_router(notion_sync_router)
sync_router.include_router(zendesk_sync_router)


# Google sync description
Expand Down Expand Up @@ -82,6 +84,12 @@
auth_method=AuthMethodEnum.URI_WITH_CALLBACK,
)

zendesk_sync = SyncsDescription(
name="Zendesk",
description="Sync your Zendesk with Quivr",
auth_method=AuthMethodEnum.URI_WITH_CALLBACK,
)


@sync_router.get(
"/sync/all",
Expand Down
52 changes: 52 additions & 0 deletions backend/api/quivr_api/modules/sync/controller/zendesk_ask_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
zendeskAskTokenPage = """
<!DOCTYPE html>
<html>
<head>
<title>Enter Zendesk API Token</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f8f9fa;
font-family: Arial, sans-serif;
}
.container {
text-align: center;
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.input-field {
margin-bottom: 20px;
}
.submit-button {
padding: 10px 20px;
font-size: 1em;
color: #fff;
background-color: #6142d4;
border: none;
border-radius: 5px;
cursor: pointer;
}
.submit-button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h2>Enter Your Zendesk API Token</h2>
<form action="/sync/zendesk/submit-token" method="post">
<div class="input-field">
<input type="text" name="api_token" placeholder="API Token" required>
</div>
<button type="submit" class="submit-button">Submit</button>
</form>
</div>
</body>
</html>
"""
161 changes: 161 additions & 0 deletions backend/api/quivr_api/modules/sync/controller/zendesk_sync_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@

from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
import random

from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse

from quivr_api.logger import get_logger
from quivr_api.middlewares.auth import AuthBearer, get_current_user
from quivr_api.modules.sync.dto.inputs import (
SyncsUserInput,
SyncsUserStatus,
SyncUserUpdateInput,
)
from quivr_api.modules.sync.service.sync_service import SyncService, SyncUserService
from quivr_api.modules.user.entity.user_identity import UserIdentity

from quivr_api.logger import get_logger
from quivr_api.middlewares.auth import AuthBearer, get_current_user
from quivr_api.modules.sync.dto.inputs import (
SyncsUserInput,
SyncsUserStatus,
SyncUserUpdateInput,
)
from quivr_api.modules.sync.service.sync_service import SyncService, SyncUserService
from quivr_api.modules.user.entity.user_identity import UserIdentity
from fastapi import Form

from .successfull_connection import successfullConnectionPage

# Initialize logger
logger = get_logger(__name__)

# Initialize sync service
sync_service = SyncService()
sync_user_service = SyncUserService()

# Initialize API router
zendesk_sync_router = APIRouter()

#


@zendesk_sync_router.post(
"/sync/zendesk/authorize",
dependencies=[Depends(AuthBearer())],
tags=["Sync"],
)
def authorize_azure(
request: Request, name: str, current_user: UserIdentity = Depends(get_current_user)
):
"""
Authorize Azure sync for the current user.

Args:
request (Request): The request object.
current_user (UserIdentity): The current authenticated user.

Returns:
dict: A dictionary containing the authorization URL.
"""

state = random.randint(100000, 999999)
sync_user_input = SyncsUserInput(
user_id=str(current_user.id),
name=name,
provider="Azure",
credentials={},
state={},
additional_data={},
status=str(SyncsUserStatus.SYNCING),
)
sync_user_service.create_sync_user(sync_user_input)
return {"authorization_url": f"http://stangirard.com:5050/sync/zendesk/enter-token?state={state}"}

@zendesk_sync_router.get("/sync/zendesk/enter-token", tags=["Sync"])
def enter_zendesk_token_page(request: Request):
"""
Serve the HTML page to enter the Zendesk API token.
"""
state = request.query_params.get("state", "")
zendeskAskTokenPage = f"""
<!DOCTYPE html>
<html>
<head>
<title>Enter Zendesk API Token</title>
<style>
body {{
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f8f9fa;
font-family: Arial, sans-serif;
}}
.container {{
text-align: center;
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}}
.input-field {{
margin-bottom: 20px;
}}
.submit-button {{
padding: 10px 20px;
font-size: 1em;
color: #fff;
background-color: #6142d4;
border: none;
border-radius: 5px;
cursor: pointer;
}}
.submit-button:hover {{
background-color: #0056b3;
}}
</style>
</head>
<body>
<div class="container">
<h2>Enter Your Zendesk API Token</h2>
<form action="/sync/zendesk/submit-token" method="post">
<div class="input-field">
<input type="text" name="api_token" placeholder="API Token" required>
</div>
<input type="hidden" name="state" value="{state}">
<button type="submit" class="submit-button">Submit</button>
</form>
</div>
</body>
</html>
"""
return zendeskAskTokenPage

@zendesk_sync_router.post("/sync/zendesk/submit-token", tags=["Sync"])
def submit_zendesk_token(api_token: str = Form(...), current_user: UserIdentity = Depends(get_current_user)):
"""
Handle the submission of the Zendesk API token.

Args:
api_token (str): The API token provided by the user.
current_user (UserIdentity): The current authenticated user.

Returns:
HTMLResponse: A success page.
"""
logger.debug(f"Received Zendesk API token for user: {current_user.id}")

# Update the sync user with the provided Zendesk API token
sync_user_input = SyncUserUpdateInput(
email=current_user.email,
credentials={"api_token": api_token},
status=str(SyncsUserStatus.SYNCED),
)
sync_user_service.update_sync_user(current_user.id, {}, sync_user_input)
logger.info(f"Zendesk API token updated successfully for user: {current_user.id}")

return HTMLResponse(successfullConnectionPage)
11 changes: 11 additions & 0 deletions frontend/lib/api/sync/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ export const syncSharepoint = async (
).data;
};

export const syncZendesk = async (
name: string,
axiosInstance: AxiosInstance
): Promise<{ authorization_url: string }> => {
return (
await axiosInstance.post<{ authorization_url: string }>(
`/sync/zendesk/authorize?name=${name}`
)
).data;
};

export const syncDropbox = async (
name: string,
axiosInstance: AxiosInstance
Expand Down
5 changes: 3 additions & 2 deletions frontend/lib/api/sync/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export type Provider = "Google" | "Azure" | "DropBox" | "Notion" | "GitHub";
export type Provider = "Google" | "Azure" | "DropBox" | "Notion" | "GitHub" | "Zendesk";

export type Integration =
| "Google Drive"
| "Share Point"
| "Dropbox"
| "Notion"
| "GitHub";
| "GitHub"
| "Zendesk"

export type SyncStatus = "SYNCING" | "SYNCED" | "ERROR" | "REMOVED";

Expand Down
8 changes: 7 additions & 1 deletion frontend/lib/api/sync/useSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
syncGoogleDrive,
syncNotion,
syncSharepoint,
updateActiveSync
syncZendesk,
updateActiveSync,
} from "./sync";
import { Integration, OpenedConnection, Provider } from "./types";

Expand All @@ -32,6 +33,8 @@ export const useSync = () => {
"https://quivr-cms.s3.eu-west-3.amazonaws.com/Notion_app_logo_004168672c.png",
GitHub:
"https://quivr-cms.s3.eu-west-3.amazonaws.com/dropbox_dce4f3d753.png",
Zendesk:
"https://quivr-cms.s3.eu-west-3.amazonaws.com/dropbox_dce4f3d753.png",
};

const integrationIconUrls: Record<Integration, string> = {
Expand All @@ -45,6 +48,8 @@ export const useSync = () => {
"https://quivr-cms.s3.eu-west-3.amazonaws.com/Notion_app_logo_004168672c.png",
GitHub:
"https://quivr-cms.s3.eu-west-3.amazonaws.com/dropbox_dce4f3d753.png",
Zendesk:
"https://quivr-cms.s3.eu-west-3.amazonaws.com/dropbox_dce4f3d753.png",
};

const getActiveSyncsForBrain = async (brainId: string) => {
Expand All @@ -59,6 +64,7 @@ export const useSync = () => {
syncSharepoint: async (name: string) => syncSharepoint(name, axiosInstance),
syncDropbox: async (name: string) => syncDropbox(name, axiosInstance),
syncNotion: async (name: string) => syncNotion(name, axiosInstance),
syncZendesk: async (name: string) => syncZendesk(name, axiosInstance),
getUserSyncs: async () => getUserSyncs(axiosInstance),
getSyncFiles: async (userSyncId: number, folderId?: string) =>
getSyncFiles(axiosInstance, userSyncId, folderId),
Expand Down
8 changes: 7 additions & 1 deletion frontend/lib/components/ConnectionCards/ConnectionCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface ConnectionCardsProps {
export const ConnectionCards = ({
fromAddKnowledge,
}: ConnectionCardsProps): JSX.Element => {
const { syncGoogleDrive, syncSharepoint, syncDropbox } =
const { syncGoogleDrive, syncSharepoint, syncDropbox, syncZendesk } =
useSync();

return (
Expand All @@ -37,6 +37,12 @@ export const ConnectionCards = ({
fromAddKnowledge={fromAddKnowledge}
oneAccountLimitation={true}
/> */}
<ConnectionSection
label="Zendesk"
provider="Zendesk"
callback={(name: string) => syncZendesk(name)}
fromAddKnowledge={fromAddKnowledge}
/>
<ConnectionSection
label="Sharepoint"
provider="Azure"
Expand Down