From 1380fbccbec044b70f2f8614706bf3d191b5bfb3 Mon Sep 17 00:00:00 2001 From: Santiago Lo Coco Date: Fri, 1 Dec 2023 14:25:14 -0300 Subject: [PATCH 1/5] Update .gitlab-ci.yml --- .gitlab-ci.yml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 62a7d47..88c7231 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,6 +14,8 @@ default: - api_failure before_script: - docker login -u $CI_REGISTRY_USER --password $CI_JOB_TOKEN $CI_REGISTRY + artifacts: + expire_in: 1 week stages: - prep @@ -70,18 +72,8 @@ preparation: dotenv: context.env .build-and-push-script: &build-and-push-script - - | - if [[ -z "${BUILD_ARG_PROD}" ]]; then - BUILD_ARG_PROD="x=x" - fi - if [[ -z "${BUILD_ARG_PROD_OTHER}" ]]; then - BUILD_ARG_PROD_OTHER="x=x" - fi - if [[ -z "${BUILD_ARG_TEST}" ]]; then - BUILD_ARG_TEST="x=x" - fi - - docker build ${FOLDER} -f ${FOLDER}/Dockerfile.prod --build-arg "${BUILD_ARG_PROD}" --build-arg "${BUILD_ARG_PROD_OTHER}" -t ${PROD_IMAGE} - - docker build ${FOLDER} -f ${FOLDER}/Dockerfile.test --build-arg "${BUILD_ARG_TEST}" -t ${TEST_IMAGE} + - docker build ${FOLDER} -f ${FOLDER}/Dockerfile.prod --build-arg "${BUILD_ARG_PROD-x}" --build-arg "${BUILD_ARG_PROD_OTHER-x}" -t ${PROD_IMAGE} + - docker build ${FOLDER} -f ${FOLDER}/Dockerfile.test -t ${TEST_IMAGE} - docker push ${PROD_IMAGE} - docker push ${TEST_IMAGE} From 6c4aa99eb7e7689217a9a60cda01ac8f990723a5 Mon Sep 17 00:00:00 2001 From: Santiago Lo Coco Date: Fri, 1 Dec 2023 16:09:21 -0300 Subject: [PATCH 2/5] Fix more bugs --- flights-domain/flights-information/requirements.txt | 5 ++--- gateway/requirements.txt | 5 ++--- gateway/src/api/main.py | 3 +++ subscription-domain/.env.dev.example | 2 +- subscription-domain/.env.prod.example | 2 +- subscription-domain/docker-template.yml | 2 +- subscription-domain/subscription-manager/requirements.txt | 5 ++--- subscription-domain/subscription-manager/src/api/config.py | 7 ++++++- .../subscription-manager/src/api/utils/telegram.py | 2 +- 9 files changed, 19 insertions(+), 14 deletions(-) diff --git a/flights-domain/flights-information/requirements.txt b/flights-domain/flights-information/requirements.txt index e03e064..a781eb0 100644 --- a/flights-domain/flights-information/requirements.txt +++ b/flights-domain/flights-information/requirements.txt @@ -4,6 +4,5 @@ psycopg2-binary==2.9.5 pyjwt==2.6.0 gunicorn==20.1.0 sqlalchemy==2.0.22 -asyncreq==0.0.5 -logmiddleware==0.0.3 -async-timeout==4.0.3 \ No newline at end of file +asyncreq==0.0.6 +logmiddleware==0.0.4 \ No newline at end of file diff --git a/gateway/requirements.txt b/gateway/requirements.txt index 21811f2..6825e9d 100644 --- a/gateway/requirements.txt +++ b/gateway/requirements.txt @@ -3,6 +3,5 @@ fastapi[all]==0.103.2 pyjwt==2.6.0 gunicorn==20.1.0 requests==2.31.0 -asyncreq==0.0.5 -logmiddleware==0.0.3 -async-timeout==4.0.3 \ No newline at end of file +asyncreq==0.0.6 +logmiddleware==0.0.4 \ No newline at end of file diff --git a/gateway/src/api/main.py b/gateway/src/api/main.py index 771e61c..a03bd2c 100644 --- a/gateway/src/api/main.py +++ b/gateway/src/api/main.py @@ -7,6 +7,9 @@ from logmiddleware import RouterLoggingMiddleware, logging_config from src.api.config import API_DEBUG from src.api.routes import auth, flights, health, notifications, subscriptions, users +# from src.api.log import RouterLoggingMiddleware, logging_config + + logging.config.dictConfig(logging_config) logger = logging.getLogger(__name__) diff --git a/subscription-domain/.env.dev.example b/subscription-domain/.env.dev.example index 767bf67..a4d18bb 100644 --- a/subscription-domain/.env.dev.example +++ b/subscription-domain/.env.dev.example @@ -2,5 +2,5 @@ POSTGRES_USER=user POSTGRES_PASS=password POSTGRES_DB=api_dev APP_SETTINGS=src.config.DevelopmentConfig -TOKEN=3275588851:AT36AGy_BChQUuCq2M6d2UrY5CSWtZe45gV +TG_TOKEN=3275588851:AT36AGy_BChQUuCq2M6d2UrY5CSWtZe45gV API_DEBUG=true \ No newline at end of file diff --git a/subscription-domain/.env.prod.example b/subscription-domain/.env.prod.example index 0078a52..bcb062a 100644 --- a/subscription-domain/.env.prod.example +++ b/subscription-domain/.env.prod.example @@ -2,5 +2,5 @@ POSTGRES_USER=user POSTGRES_PASS=password POSTGRES_DB=api_prod APP_SETTINGS=src.config.ProductionConfig -TOKEN=3275588851:AT36AGy_BChQUuCq2M6d2UrY5CSWtZe45gV +TG_TOKEN=3275588851:AT36AGy_BChQUuCq2M6d2UrY5CSWtZe45gV API_DEBUG=false \ No newline at end of file diff --git a/subscription-domain/docker-template.yml b/subscription-domain/docker-template.yml index a2d693e..527a8f2 100644 --- a/subscription-domain/docker-template.yml +++ b/subscription-domain/docker-template.yml @@ -15,7 +15,7 @@ services: - PORT=5000 - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASS}@subscriptions-db/${POSTGRES_DB} - APP_SETTINGS=${APP_SETTINGS} - - TOKEN=${TOKEN} + - TG_TOKEN=${TG_TOKEN} logging: driver: gelf options: diff --git a/subscription-domain/subscription-manager/requirements.txt b/subscription-domain/subscription-manager/requirements.txt index e03e064..a781eb0 100644 --- a/subscription-domain/subscription-manager/requirements.txt +++ b/subscription-domain/subscription-manager/requirements.txt @@ -4,6 +4,5 @@ psycopg2-binary==2.9.5 pyjwt==2.6.0 gunicorn==20.1.0 sqlalchemy==2.0.22 -asyncreq==0.0.5 -logmiddleware==0.0.3 -async-timeout==4.0.3 \ No newline at end of file +asyncreq==0.0.6 +logmiddleware==0.0.4 \ No newline at end of file diff --git a/subscription-domain/subscription-manager/src/api/config.py b/subscription-domain/subscription-manager/src/api/config.py index 8b894cf..ea64a9e 100644 --- a/subscription-domain/subscription-manager/src/api/config.py +++ b/subscription-domain/subscription-manager/src/api/config.py @@ -1,4 +1,9 @@ import os -API_FLIGHTS = "http://fids_flights_api:5000/flights" +TEST_TARGET = os.getenv("TEST_TARGET") API_DEBUG = os.getenv("API_DEBUG") + +if TEST_TARGET == "INTEGRATION": + API_FLIGHTS = "http://fids-flights-dev_flights-api:5000/flights" +else: + API_FLIGHTS = "http://fids-flights_flights-api:5000/flights" diff --git a/subscription-domain/subscription-manager/src/api/utils/telegram.py b/subscription-domain/subscription-manager/src/api/utils/telegram.py index 7795b53..13c930c 100644 --- a/subscription-domain/subscription-manager/src/api/utils/telegram.py +++ b/subscription-domain/subscription-manager/src/api/utils/telegram.py @@ -2,7 +2,7 @@ import os from asyncreq import request -TOKEN = os.getenv("TOKEN") +TOKEN = os.getenv("TG_TOKEN") async def send_message(chat_id, message): From dc7c6f743952abc6cf50e29c29baeaa831fa3067 Mon Sep 17 00:00:00 2001 From: Santiago Lo Coco Date: Fri, 1 Dec 2023 19:30:41 -0300 Subject: [PATCH 3/5] Add subscribe/unsuscribe logic in browser-domain Also, fix some backend bugs --- browser-domain/src/Api.ts | 35 ++++- browser-domain/src/Types.d.ts | 5 + .../src/components/Home/Card/Card.tsx | 143 +++++++++++++++++- browser-domain/src/components/Home/Home.tsx | 33 +++- browser-domain/src/components/LogIn/LogIn.tsx | 3 +- .../src/hooks/useFetchSubscriptions.tsx | 46 ++++++ browser-domain/src/useAuth.tsx | 10 +- gateway/src/api/routes/flights.py | 4 +- gateway/src/api/routes/notifications.py | 24 ++- gateway/src/api/routes/subscriptions.py | 41 ++++- .../src/api/cruds/subscription.py | 15 +- .../src/api/routes/notifications.py | 23 ++- .../src/api/routes/subscriptions.py | 41 +++-- .../src/api/utils/messages.py | 15 +- 14 files changed, 404 insertions(+), 34 deletions(-) create mode 100644 browser-domain/src/hooks/useFetchSubscriptions.tsx diff --git a/browser-domain/src/Api.ts b/browser-domain/src/Api.ts index e0801c9..a490e88 100644 --- a/browser-domain/src/Api.ts +++ b/browser-domain/src/Api.ts @@ -1,5 +1,5 @@ import { Axios, AxiosError } from "axios"; -import { Credentials, Token, User, Flight, FlightCreate } from "./Types"; +import { Credentials, Token, User, Flight, FlightCreate, SubscriptionsCreate } from "./Types"; const instance = new Axios({ baseURL: process.env.REACT_APP_ENDPOINT ? process.env.REACT_APP_ENDPOINT : "http://127.0.0.1:5000/", @@ -24,6 +24,8 @@ instance.interceptors.response.use( json["count"] = response.headers["x-count"] console.log(json) return json + } else if (response.status == 204) { + return response; } return JSON.parse(response.data); }, @@ -75,4 +77,35 @@ export const createFlight = ( return instance.post("flights", flight_data, { headers: { Authorization: `Bearer ${token}` }, }); +}; + +export const subscribeToFlight = (subscription: SubscriptionsCreate, token: string): Promise => { + return instance.post("subscriptions", subscription, { + headers: { Authorization: `Bearer ${token}` }, + }); +}; + +export const getChatId = (user_id: number, token: string): Promise => { + return instance.get("notifications?user_id=" + user_id, { + headers: { Authorization: `Bearer ${token}` }, + }); +}; + +export const getSubscription = (subscription: SubscriptionsCreate, token: string): Promise => { + return instance.get("subscriptions?user_id=" + subscription.user_id + "&flight_id=" +subscription.flight_id, { + headers: { Authorization: `Bearer ${token}` }, + }); +}; + +export const unsubscribeFromFlight = (subscription: SubscriptionsCreate, token: string): Promise => { + return instance.delete("subscriptions", { + headers: { Authorization: `Bearer ${token}`}, + data: subscription + }); +}; + +export const fetchSubscriptions = (user_id: number, token: string): Promise => { + return instance.get("subscriptions?user_id=" + user_id, { + headers: { Authorization: `Bearer ${token}` }, + }); }; \ No newline at end of file diff --git a/browser-domain/src/Types.d.ts b/browser-domain/src/Types.d.ts index 6b19936..81b8ce7 100644 --- a/browser-domain/src/Types.d.ts +++ b/browser-domain/src/Types.d.ts @@ -45,4 +45,9 @@ export interface FlightCreate { departure_time: string; arrival_time: string; gate: string; +} + +export interface SubscriptionsCreate { + flight_id: number; + user_id: number; } \ No newline at end of file diff --git a/browser-domain/src/components/Home/Card/Card.tsx b/browser-domain/src/components/Home/Card/Card.tsx index 0a78378..78e92d2 100644 --- a/browser-domain/src/components/Home/Card/Card.tsx +++ b/browser-domain/src/components/Home/Card/Card.tsx @@ -1,10 +1,14 @@ -import React from "react"; -import { Avatar, Space, Typography, Tag } from "antd"; +import React, { useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import { Avatar, Space, Typography, Tag, Button, Modal } from "antd"; import { RightOutlined, ClockCircleOutlined, SwapOutlined, EnvironmentOutlined, CalendarOutlined } from "@ant-design/icons"; import "./Card.css"; +import { getChatId, getSubscription, subscribeToFlight, unsubscribeFromFlight } from "../../../Api"; +import { User } from "../../../Types"; interface FlightProps { + id: number; flight_code: string; status: string; origin: string; @@ -16,11 +20,110 @@ interface FlightProps { interface CardProps { flight: FlightProps; + user: User | undefined; + subscribed: boolean; + refresh: any; } const { Text } = Typography; -export const Card: React.FC = ({ flight }) => { +export const Card: React.FC = ({ flight, user, subscribed, refresh }) => { + // const [error, setError] = useState(null); + // const [subscribed, setSubscribed] = useState(false); + + // useEffect(() => { + // setError(null); + + // const token = localStorage.getItem("token"); + // if (!token || !user) { + // setError("No token!"); + // return; + // } + + // const data = { + // user_id: user.id, + // flight_id: flight.id + // } + + // getSubscription(data, token) + // .then((data) => { + // setSubscribed(true); + // }) + // .catch((error) => { + // setError(error as string); + // }); + // }, [user]); + const [modalVisible, setModalVisible] = useState(false); + + + const handleSubscribe = async (event: React.FormEvent) => { + event.preventDefault(); + + // setError(null); + + const token = localStorage.getItem("token"); + if (!token || !user) { + // setError("No token!"); + return; + } + + const data = { + user_id: user.id, + flight_id: flight.id + } + + console.log(data) + + subscribeToFlight(data, token) + .then(() => { + refresh() + getChatId(user.id, token) + .then(() => {}) + .catch((error) => { + console.log("NO CHAT") + setModalVisible(true); + // setError(error as string); + }) + }) + .catch((error) => { + // setError(error as string); + }); + }; + + const handleModalClose = () => { + setModalVisible(false); + }; + + const handleUnsubscribe = async (event: React.FormEvent) => { + event.preventDefault(); + + // setError(null); + + const token = localStorage.getItem("token"); + if (!token || !user) { + // setError("No token!"); + return; + } + + const data = { + user_id: user.id, + flight_id: flight.id + } + + console.log(data) + + unsubscribeFromFlight(data, token) + .then(() => { + console.log("?") + refresh() + }) + .catch((error) => { + // setError(error as string); + }); + }; + + console.log(subscribed) + return (
@@ -32,7 +135,7 @@ export const Card: React.FC = ({ flight }) => {
- + Status: {flight.status} @@ -50,11 +153,41 @@ export const Card: React.FC = ({ flight }) => { {flight.arrival_time} - + Gate: {flight.gate} + + ID: + {flight.id} +
+ {!(subscribed) ? + + : + + } + + OK + + ]} + > +

To start receiving messages open this link on your smartphone: + +

+ + {`https://t.me/fids_system_bot?start=${user?.id}`} + +
); }; diff --git a/browser-domain/src/components/Home/Home.tsx b/browser-domain/src/components/Home/Home.tsx index 8649271..2c252b9 100644 --- a/browser-domain/src/components/Home/Home.tsx +++ b/browser-domain/src/components/Home/Home.tsx @@ -1,9 +1,11 @@ import React, { useEffect, useState } from "react"; import { Card } from "./Card/Card"; import { useFetchFlights } from "../../hooks/useFetchFlights"; -import { Flight } from "../../Types"; +import { Flight, SubscriptionsCreate } from "../../Types"; import { useNavigate } from "react-router"; import useAuth from "../../useAuth"; +import { useFetchSubscriptions } from "../../hooks/useFetchSubscriptions"; +import { fetchSubscriptions } from "../../Api"; interface Props { flights?: Flight[]; @@ -17,7 +19,9 @@ export const Home: React.FC = (props) => { const navigate = useNavigate() const [currentPage, setCurrentPage] = useState(initialPage); - const { loading, isAirline } = useAuth(); + const { loading, isAirline, user, token } = useAuth(); + + const { subscriptions, loading: subsLoading, fetchData } = useFetchSubscriptions(user, token); useEffect(() => { const newParams = new URLSearchParams(window.location.search); @@ -25,6 +29,25 @@ export const Home: React.FC = (props) => { navigate(`?${newParams.toString()}`); }, [currentPage, navigate]); + // const [errorSub, setErrorSub] = useState(null); + // const [subscriptions, setSubscriptions] = useState([]); + + // useEffect(() => { + // setErrorSub(null); + + // console.log(user) + // console.log(token) + // if (!user || !token) { + // return; + // } + + // fetchSubscriptions(user.id, token) + // .then((data) => { + // setSubscriptions(data); + // }) + // .catch((error) => { }); + // }, [user, token, loading]); + const goToPrevPage = () => { if (currentPage > 1) { setCurrentPage(currentPage - 1); @@ -47,13 +70,15 @@ export const Home: React.FC = (props) => { return
Loading...
; } + // console.log(subscriptions) + return (
{isAirline ? : <>}

Flights

- {(props.flights ? props.flights : flights).map((u) => { - return ; + {(props.flights ? props.flights : flights).map((f) => { + return i.flight_id === f.id))} refresh={fetchData} />; })} {error ?
{error}
: <>}
diff --git a/browser-domain/src/components/LogIn/LogIn.tsx b/browser-domain/src/components/LogIn/LogIn.tsx index 81a88c6..79ed4fe 100644 --- a/browser-domain/src/components/LogIn/LogIn.tsx +++ b/browser-domain/src/components/LogIn/LogIn.tsx @@ -8,6 +8,7 @@ export const LogIn = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const navigate = useNavigate(); + console.log(error) return (
@@ -39,7 +40,7 @@ export const LogIn = () => { Sign up {error ? ( -
{error}
+
{error?.message}
) : ( <> )} diff --git a/browser-domain/src/hooks/useFetchSubscriptions.tsx b/browser-domain/src/hooks/useFetchSubscriptions.tsx new file mode 100644 index 0000000..99ed464 --- /dev/null +++ b/browser-domain/src/hooks/useFetchSubscriptions.tsx @@ -0,0 +1,46 @@ +import React, { useEffect, useCallback } from "react"; +import { useState } from "react"; +import { User, Flight, SubscriptionsCreate } from "../Types"; +import { fetchSubscriptions } from "../Api"; + +export const useFetchSubscriptions = (user: User | undefined, token: string | undefined) => { + const [error, setError] = useState(null); + const [subscriptions, setSubscriptions] = useState([]); + const [loading, setLoading] = useState(true); + + const fetchData = useCallback(async () => { + setError(null); + + if (!user || !token || !loading) { + return; + } + + fetchSubscriptions(user.id, token) + .then((data) => { + setSubscriptions(data); + setLoading(false) + }) + .catch((error) => { }); + }, [user, token]); + + useEffect(() => { + fetchData() + }, [fetchData]); + + // useEffect(() => { + // setError(null); + + // if (!user || !token || !loading) { + // return; + // } + + // fetchSubscriptions(user.id, token) + // .then((data) => { + // setSubscriptions(data); + // setLoading(false) + // }) + // .catch((error) => { }); + // }, [user, token]); + + return { subscriptions, error, loading, fetchData }; +}; diff --git a/browser-domain/src/useAuth.tsx b/browser-domain/src/useAuth.tsx index 98e63e2..9d8e993 100644 --- a/browser-domain/src/useAuth.tsx +++ b/browser-domain/src/useAuth.tsx @@ -8,6 +8,7 @@ interface AuthContextType { user?: User; loading: boolean; isAirline: boolean; + token?: string; error?: any; login: (credentials: Credentials) => void; signUp: (email: string, name: string, password: string) => void; @@ -25,6 +26,7 @@ export function AuthProvider({ }): JSX.Element { const [user, setUser] = useState(); const [error, setError] = useState(); + const [token, setToken] = useState(); const [loading, setLoading] = useState(false); const [loadingInitial, setLoadingInitial] = useState(true); const [isAirline, setIsAirline] = useState(false); @@ -51,7 +53,10 @@ export function AuthProvider({ .then((res) => fetchUserById(res.id, existingToken) .then((res) => setUser(res)) .catch((_error) => { }) - .finally(() => setLoadingInitial(false)) + .finally(() => { + setToken(existingToken) + setLoadingInitial(false) + }) ) .catch((_error) => { setLoadingInitial(false) @@ -73,6 +78,7 @@ export function AuthProvider({ const user = fetchUserById(x.user_id as number, x.access_token) .then(y => { setUser(y); + setToken(x.access_token) navigate("/home") }) .catch((error) => setError(error)) @@ -87,6 +93,7 @@ export function AuthProvider({ function logout() { localStorage.removeItem("token"); setUser(undefined); + setToken(undefined) navigate("/login") } @@ -95,6 +102,7 @@ export function AuthProvider({ user, loading, isAirline, + token, error, login, signUp, diff --git a/gateway/src/api/routes/flights.py b/gateway/src/api/routes/flights.py index cc97a82..2e67137 100644 --- a/gateway/src/api/routes/flights.py +++ b/gateway/src/api/routes/flights.py @@ -49,9 +49,9 @@ async def update_flight( req: Request, authorization: Annotated[str | None, Header()] = None, ): - id = await checkAuth(req, authorization, isAirline=True) + user_id = await checkAuth(req, authorization, isAirline=True) update = flight_update.model_dump() - update["user_id"] = id + update["user_id"] = user_id request_id = req.state.request_id header = {"x-api-request-id": request_id} (response, status, _) = await request( diff --git a/gateway/src/api/routes/notifications.py b/gateway/src/api/routes/notifications.py index 102b4a3..09d07ee 100644 --- a/gateway/src/api/routes/notifications.py +++ b/gateway/src/api/routes/notifications.py @@ -1,7 +1,10 @@ +from typing import Annotated + from asyncreq import request -from fastapi import APIRouter, HTTPException, Request +from fastapi import APIRouter, Header, HTTPException, Request from src.api.config import API_NOTIFICATIONS +from src.api.routes.auth import checkAuth from src.api.schemas.notification import Update as Message router = APIRouter() @@ -18,3 +21,22 @@ async def receive_message(message: Message, req: Request): if status < 200 or status > 204: raise HTTPException(status_code=status, detail=response) return response + + +@router.get("") +async def get_chat_by_user_id( + req: Request, + user_id: int, + authorization: Annotated[str | None, Header()] = None, +): + await checkAuth(req, authorization) + query = {} + query["user_id"] = user_id + request_id = req.state.request_id + header = {"x-api-request-id": request_id} + (response, status, _) = await request( + f"{API_NOTIFICATIONS}", "GET", query=query, headers=header + ) + if status < 200 or status > 204: + raise HTTPException(status_code=status, detail=response) + return response diff --git a/gateway/src/api/routes/subscriptions.py b/gateway/src/api/routes/subscriptions.py index 4a74fe0..f5142cc 100644 --- a/gateway/src/api/routes/subscriptions.py +++ b/gateway/src/api/routes/subscriptions.py @@ -1,4 +1,4 @@ -from typing import Annotated +from typing import Annotated, Optional from asyncreq import request from fastapi import APIRouter, Header, HTTPException, Request @@ -25,3 +25,42 @@ async def create_subscription( if status < 200 or status > 204: raise HTTPException(status_code=status, detail=response) return response + + +@router.delete("") +async def delete_subscription( + subscription: Subscription, + req: Request, + authorization: Annotated[str | None, Header()] = None, +): + await checkAuth(req, authorization) + request_id = req.state.request_id + header = {"x-api-request-id": request_id} + (response, status, _) = await request( + f"{API_SUBSCRIPTIONS}", "DELETE", json=subscription.model_dump(), headers=header + ) + if status < 200 or status > 204: + raise HTTPException(status_code=status, detail=response) + return response + + +@router.get("") +async def get_subscriptions( + req: Request, + user_id: int, + flight_id: Optional[int] = None, + authorization: Annotated[str | None, Header()] = None, +): + await checkAuth(req, authorization) + query = {} + query["user_id"] = user_id + if flight_id: + query["flight_id"] = flight_id + request_id = req.state.request_id + header = {"x-api-request-id": request_id} + (response, status, _) = await request( + f"{API_SUBSCRIPTIONS}", "GET", query=query, headers=header + ) + if status < 200 or status > 204: + raise HTTPException(status_code=status, detail=response) + return response diff --git a/subscription-domain/subscription-manager/src/api/cruds/subscription.py b/subscription-domain/subscription-manager/src/api/cruds/subscription.py index 0ce6d61..ae59c9c 100644 --- a/subscription-domain/subscription-manager/src/api/cruds/subscription.py +++ b/subscription-domain/subscription-manager/src/api/cruds/subscription.py @@ -9,7 +9,20 @@ def get_subscriptions(db: Session, user_id: int): return db.query(Subscription).filter(Subscription.user_id == user_id).all() +def get_subscription(db: Session, user_id: int, flight_id: int): + return ( + db.query(Subscription) + .filter(Subscription.user_id == user_id, Subscription.flight_id == flight_id) + .first() + ) + + def create_subscription(db: Session, subscription: SubscriptionPydantic): + if get_subscription( + db, user_id=subscription.user_id, flight_id=subscription.flight_id + ): + raise ValueError + db_subscription = Subscription( user_id=subscription.user_id, flight_id=subscription.flight_id, @@ -22,7 +35,7 @@ def create_subscription(db: Session, subscription: SubscriptionPydantic): def remove_subscription(db: Session, user_id: int, flight_id: int): db.query(Subscription).filter( - Subscription.user_id == user_id and Subscription.flight_id == flight_id + Subscription.user_id == user_id, Subscription.flight_id == flight_id ).delete() db.commit() diff --git a/subscription-domain/subscription-manager/src/api/routes/notifications.py b/subscription-domain/subscription-manager/src/api/routes/notifications.py index 20ca2a9..c453361 100644 --- a/subscription-domain/subscription-manager/src/api/routes/notifications.py +++ b/subscription-domain/subscription-manager/src/api/routes/notifications.py @@ -1,7 +1,7 @@ import re from asyncreq import request -from fastapi import APIRouter, BackgroundTasks, Depends, Response +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Response from sqlalchemy.orm import Session from src.api.config import API_FLIGHTS @@ -10,11 +10,15 @@ from src.api.cruds import subscription as subs_crud from src.api.db import get_db from src.api.schemas.chat import Chat, Update from src.api.utils import telegram -from src.api.utils.messages import get_flight_message, get_invalid_message +from src.api.utils.messages import ( + get_flight_message, + get_invalid_message, + get_start_message, +) router = APIRouter() -msg_options = re.compile(r"^/(flight \d+|stop|start)$") +msg_options = re.compile(r"^/(flight \d+|stop|start \d+)$") @router.post("") @@ -33,8 +37,11 @@ async def create_chat( action = text.partition(" ")[0] if action == "/start": user_id = int(message["text"].partition(" ")[2]) - new_chat = Chat(chat_id=str(message["chat"]["id"]), user_id=user_id) + chat_id = str(message["chat"]["id"]) + new_chat = Chat(chat_id=chat_id, user_id=user_id) notif_crud.create_chat(db=db, chat=new_chat) + msg = get_start_message() + background_tasks.add_task(telegram.send_message, chat_id, msg) elif action == "/stop": chat_id = str(message["chat"]["id"]) user_id = notif_crud.get_user_from_chat(db=db, chat_id=chat_id).user_id @@ -50,3 +57,11 @@ async def create_chat( background_tasks.add_task(telegram.send_message, chat_id, msg) return Response(status_code=204) + + +@router.get("") +def get_chat_by_user_id(user_id: int, db: Session = Depends(get_db)): + db_chat = notif_crud.get_chat_id(db=db, user_id=user_id) + if db_chat is None: + raise HTTPException(status_code=404, detail="Chat not found") + return db_chat diff --git a/subscription-domain/subscription-manager/src/api/routes/subscriptions.py b/subscription-domain/subscription-manager/src/api/routes/subscriptions.py index b67d1b5..c4b1866 100644 --- a/subscription-domain/subscription-manager/src/api/routes/subscriptions.py +++ b/subscription-domain/subscription-manager/src/api/routes/subscriptions.py @@ -1,31 +1,52 @@ +from typing import Optional + from fastapi import APIRouter, Depends, HTTPException, Response from sqlalchemy.orm import Session from src.api.cruds import subscription as sub_crud from src.api.db import get_db -from src.api.schemas.subscription import Subscription, SubscriptionRemove +from src.api.schemas.subscription import Subscription router = APIRouter() @router.post("") def create_subscription(subscription: Subscription, db: Session = Depends(get_db)): - return sub_crud.create_subscription(db=db, subscription=subscription) + try: + db_subscription = sub_crud.create_subscription(db=db, subscription=subscription) + except ValueError: + raise HTTPException(status_code=409, detail="User already suscribed") + # if notif_crud.get_chat_id(subscription.user_id) is None: + # raise HTTPException(status_code=424, detail="First you need to create a chat") + return db_subscription -@router.get("/{user_id}", response_model=list[Subscription]) -def get_subscriptions(user_id: int, db: Session = Depends(get_db)): - db_subscriptions = sub_crud.get_subscriptions(db=db, user_id=user_id) +# @router.get("/{user_id}", response_model=list[Subscription]) +# def get_subscriptions(user_id: int, db: Session = Depends(get_db)): +# db_subscriptions = sub_crud.get_subscriptions(db=db, user_id=user_id) +# if db_subscriptions is None: +# raise HTTPException(status_code=404, detail="Subscription not found") +# return db_subscriptions + + +@router.get("") +def get_subscription( + user_id: int, flight_id: Optional[int] = None, db: Session = Depends(get_db) +): + if flight_id: + db_subscriptions = sub_crud.get_subscription( + db=db, user_id=user_id, flight_id=flight_id + ) + else: + db_subscriptions = sub_crud.get_subscriptions(db=db, user_id=user_id) if db_subscriptions is None: raise HTTPException(status_code=404, detail="Subscription not found") return db_subscriptions -@router.delete("/{user_id}") -def delete_subscription( - user_id: int, subscription: SubscriptionRemove, db: Session = Depends(get_db) -): +@router.delete("") +def delete_subscription(subscription: Subscription, db: Session = Depends(get_db)): sub_crud.remove_subscription( - db=db, user_id=user_id, flight_id=subscription.flight_id + db=db, user_id=subscription.user_id, flight_id=subscription.flight_id ) return Response(status_code=204) diff --git a/subscription-domain/subscription-manager/src/api/utils/messages.py b/subscription-domain/subscription-manager/src/api/utils/messages.py index e131d63..83f8cb9 100644 --- a/subscription-domain/subscription-manager/src/api/utils/messages.py +++ b/subscription-domain/subscription-manager/src/api/utils/messages.py @@ -30,7 +30,16 @@ def get_flight_message(flight: dict): def get_invalid_message(): return ( "Invalid option!\nPlease use:\n" - "\n/flights NUMBER (e.g., /flights 1) for flight details" - "\n/start to start receiving messages" - "\n/stop to manage updates." + "\n/flight NUMBER (e.g., /flight 1) for flight details" + # "\n/start to start receiving messages" + "\n/stop to stop receiving updates." + ) + + +def get_start_message(): + return ( + "Thanks for using fids! You will now start getting updates from your subscriptions!\n" + "Meanwhile you can type:\n" + "\n/flight NUMBER (e.g., /flight 1) for flight details" + "\n/stop to stop receiving updates." ) From 6d1389eb8eeb567cba7bcea75c2f216917cdf4e0 Mon Sep 17 00:00:00 2001 From: Santiago Lo Coco Date: Fri, 1 Dec 2023 19:34:15 -0300 Subject: [PATCH 4/5] Remove unused code --- .../src/components/Home/Card/Card.tsx | 36 ------------------- browser-domain/src/components/Home/Home.tsx | 24 +------------ .../src/hooks/useFetchSubscriptions.tsx | 15 -------- gateway/src/api/main.py | 3 -- .../src/api/routes/subscriptions.py | 10 ------ .../src/api/utils/messages.py | 1 - 6 files changed, 1 insertion(+), 88 deletions(-) diff --git a/browser-domain/src/components/Home/Card/Card.tsx b/browser-domain/src/components/Home/Card/Card.tsx index 78e92d2..6900065 100644 --- a/browser-domain/src/components/Home/Card/Card.tsx +++ b/browser-domain/src/components/Home/Card/Card.tsx @@ -28,42 +28,14 @@ interface CardProps { const { Text } = Typography; export const Card: React.FC = ({ flight, user, subscribed, refresh }) => { - // const [error, setError] = useState(null); - // const [subscribed, setSubscribed] = useState(false); - - // useEffect(() => { - // setError(null); - - // const token = localStorage.getItem("token"); - // if (!token || !user) { - // setError("No token!"); - // return; - // } - - // const data = { - // user_id: user.id, - // flight_id: flight.id - // } - - // getSubscription(data, token) - // .then((data) => { - // setSubscribed(true); - // }) - // .catch((error) => { - // setError(error as string); - // }); - // }, [user]); const [modalVisible, setModalVisible] = useState(false); const handleSubscribe = async (event: React.FormEvent) => { event.preventDefault(); - // setError(null); - const token = localStorage.getItem("token"); if (!token || !user) { - // setError("No token!"); return; } @@ -80,13 +52,10 @@ export const Card: React.FC = ({ flight, user, subscribed, refresh }) getChatId(user.id, token) .then(() => {}) .catch((error) => { - console.log("NO CHAT") setModalVisible(true); - // setError(error as string); }) }) .catch((error) => { - // setError(error as string); }); }; @@ -97,11 +66,8 @@ export const Card: React.FC = ({ flight, user, subscribed, refresh }) const handleUnsubscribe = async (event: React.FormEvent) => { event.preventDefault(); - // setError(null); - const token = localStorage.getItem("token"); if (!token || !user) { - // setError("No token!"); return; } @@ -114,11 +80,9 @@ export const Card: React.FC = ({ flight, user, subscribed, refresh }) unsubscribeFromFlight(data, token) .then(() => { - console.log("?") refresh() }) .catch((error) => { - // setError(error as string); }); }; diff --git a/browser-domain/src/components/Home/Home.tsx b/browser-domain/src/components/Home/Home.tsx index 2c252b9..b669c66 100644 --- a/browser-domain/src/components/Home/Home.tsx +++ b/browser-domain/src/components/Home/Home.tsx @@ -1,11 +1,10 @@ import React, { useEffect, useState } from "react"; import { Card } from "./Card/Card"; import { useFetchFlights } from "../../hooks/useFetchFlights"; -import { Flight, SubscriptionsCreate } from "../../Types"; +import { Flight } from "../../Types"; import { useNavigate } from "react-router"; import useAuth from "../../useAuth"; import { useFetchSubscriptions } from "../../hooks/useFetchSubscriptions"; -import { fetchSubscriptions } from "../../Api"; interface Props { flights?: Flight[]; @@ -29,25 +28,6 @@ export const Home: React.FC = (props) => { navigate(`?${newParams.toString()}`); }, [currentPage, navigate]); - // const [errorSub, setErrorSub] = useState(null); - // const [subscriptions, setSubscriptions] = useState([]); - - // useEffect(() => { - // setErrorSub(null); - - // console.log(user) - // console.log(token) - // if (!user || !token) { - // return; - // } - - // fetchSubscriptions(user.id, token) - // .then((data) => { - // setSubscriptions(data); - // }) - // .catch((error) => { }); - // }, [user, token, loading]); - const goToPrevPage = () => { if (currentPage > 1) { setCurrentPage(currentPage - 1); @@ -70,8 +50,6 @@ export const Home: React.FC = (props) => { return
Loading...
; } - // console.log(subscriptions) - return (
{isAirline ? : <>} diff --git a/browser-domain/src/hooks/useFetchSubscriptions.tsx b/browser-domain/src/hooks/useFetchSubscriptions.tsx index 99ed464..a9c7591 100644 --- a/browser-domain/src/hooks/useFetchSubscriptions.tsx +++ b/browser-domain/src/hooks/useFetchSubscriptions.tsx @@ -27,20 +27,5 @@ export const useFetchSubscriptions = (user: User | undefined, token: string | u fetchData() }, [fetchData]); - // useEffect(() => { - // setError(null); - - // if (!user || !token || !loading) { - // return; - // } - - // fetchSubscriptions(user.id, token) - // .then((data) => { - // setSubscriptions(data); - // setLoading(false) - // }) - // .catch((error) => { }); - // }, [user, token]); - return { subscriptions, error, loading, fetchData }; }; diff --git a/gateway/src/api/main.py b/gateway/src/api/main.py index a03bd2c..771e61c 100644 --- a/gateway/src/api/main.py +++ b/gateway/src/api/main.py @@ -7,9 +7,6 @@ from logmiddleware import RouterLoggingMiddleware, logging_config from src.api.config import API_DEBUG from src.api.routes import auth, flights, health, notifications, subscriptions, users -# from src.api.log import RouterLoggingMiddleware, logging_config - - logging.config.dictConfig(logging_config) logger = logging.getLogger(__name__) diff --git a/subscription-domain/subscription-manager/src/api/routes/subscriptions.py b/subscription-domain/subscription-manager/src/api/routes/subscriptions.py index c4b1866..93ecde6 100644 --- a/subscription-domain/subscription-manager/src/api/routes/subscriptions.py +++ b/subscription-domain/subscription-manager/src/api/routes/subscriptions.py @@ -16,19 +16,9 @@ def create_subscription(subscription: Subscription, db: Session = Depends(get_db db_subscription = sub_crud.create_subscription(db=db, subscription=subscription) except ValueError: raise HTTPException(status_code=409, detail="User already suscribed") - # if notif_crud.get_chat_id(subscription.user_id) is None: - # raise HTTPException(status_code=424, detail="First you need to create a chat") return db_subscription -# @router.get("/{user_id}", response_model=list[Subscription]) -# def get_subscriptions(user_id: int, db: Session = Depends(get_db)): -# db_subscriptions = sub_crud.get_subscriptions(db=db, user_id=user_id) -# if db_subscriptions is None: -# raise HTTPException(status_code=404, detail="Subscription not found") -# return db_subscriptions - - @router.get("") def get_subscription( user_id: int, flight_id: Optional[int] = None, db: Session = Depends(get_db) diff --git a/subscription-domain/subscription-manager/src/api/utils/messages.py b/subscription-domain/subscription-manager/src/api/utils/messages.py index 83f8cb9..e325c59 100644 --- a/subscription-domain/subscription-manager/src/api/utils/messages.py +++ b/subscription-domain/subscription-manager/src/api/utils/messages.py @@ -31,7 +31,6 @@ def get_invalid_message(): return ( "Invalid option!\nPlease use:\n" "\n/flight NUMBER (e.g., /flight 1) for flight details" - # "\n/start to start receiving messages" "\n/stop to stop receiving updates." ) From d8213f486d704a94463586c6ed460763c36a4244 Mon Sep 17 00:00:00 2001 From: Santiago Lo Coco Date: Fri, 1 Dec 2023 19:46:56 -0300 Subject: [PATCH 5/5] Update "RBAC" --- gateway/src/api/routes/notifications.py | 2 +- gateway/src/api/routes/subscriptions.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gateway/src/api/routes/notifications.py b/gateway/src/api/routes/notifications.py index 09d07ee..d2ddd81 100644 --- a/gateway/src/api/routes/notifications.py +++ b/gateway/src/api/routes/notifications.py @@ -29,7 +29,7 @@ async def get_chat_by_user_id( user_id: int, authorization: Annotated[str | None, Header()] = None, ): - await checkAuth(req, authorization) + await checkAuth(req, authorization, userId=user_id) query = {} query["user_id"] = user_id request_id = req.state.request_id diff --git a/gateway/src/api/routes/subscriptions.py b/gateway/src/api/routes/subscriptions.py index f5142cc..07b2a10 100644 --- a/gateway/src/api/routes/subscriptions.py +++ b/gateway/src/api/routes/subscriptions.py @@ -4,7 +4,7 @@ from asyncreq import request from fastapi import APIRouter, Header, HTTPException, Request from src.api.config import API_SUBSCRIPTIONS -from src.api.routes.auth import status as checkAuth +from src.api.routes.auth import checkAuth from src.api.schemas.subscriptions import Subscription router = APIRouter() @@ -16,7 +16,7 @@ async def create_subscription( req: Request, authorization: Annotated[str | None, Header()] = None, ): - await checkAuth(req, authorization) + await checkAuth(req, authorization, userId=subscription.user_id) request_id = req.state.request_id header = {"x-api-request-id": request_id} (response, status, _) = await request( @@ -33,7 +33,7 @@ async def delete_subscription( req: Request, authorization: Annotated[str | None, Header()] = None, ): - await checkAuth(req, authorization) + await checkAuth(req, authorization, userId=subscription.user_id) request_id = req.state.request_id header = {"x-api-request-id": request_id} (response, status, _) = await request( @@ -51,7 +51,7 @@ async def get_subscriptions( flight_id: Optional[int] = None, authorization: Annotated[str | None, Header()] = None, ): - await checkAuth(req, authorization) + await checkAuth(req, authorization, userId=user_id) query = {} query["user_id"] = user_id if flight_id: