Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT: Unified Notifications #290

Open
wants to merge 44 commits into
base: main
Choose a base branch
from

Conversation

jagadeeswaran-zipstack
Copy link
Contributor

@jagadeeswaran-zipstack jagadeeswaran-zipstack commented Apr 25, 2024

What

Unified Notifications consists of the following changes:

  1. Merging both log and notification messages to display them in the bottom logs component.
  2. Persisting both log and notification messages in Redis with a TTL of 1 hour.
  3. Making the logs component resizable, allowing users to drag and resize the component.
  4. Improving the styling of the logs component.

Why

The unified notification changes were implemented to enhance UX. Users can now see log and notification messages in one place, and these messages are persisted for 1 hour.

How

  1. Used Redis to store log and notification messages.
  2. Improved the styling of the components by using Antd components and additional custom styling.

Can this PR break any existing features. If yes please list of possible items. If no please exaplin why. (PS: Admins do not merge the PR without this section filled)

Yes, this PR can break existing features. The code changes have been made in quite a few existing modules, increasing the chances of regression.

Database Migrations

NA

Env Config

LOGS_EXPIRATION_TIME_IN_SECOND - This env variable stores the TTL in seconds. The default value is 3600 (1 hour).

Relevant Docs

NA

Related Issues or PRs

NA

Dependencies Versions

NA

Notes on Testing

NA

Screenshots

image
image
image

Checklist

I have read and understood the Contribution Guidelines.

Copy link
Contributor

@chandrasekharan-zipstack chandrasekharan-zipstack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BE changes LGTM

@@ -259,6 +260,8 @@ def make_user_organization_display_name(self, user_name: str) -> str:
return self.auth_service.make_user_organization_display_name(user_name)

def user_logout(self, request: Request) -> Response:
session_id: str = request.COOKIES.get("sessionid")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: We should use constants for these if we aren't already. Also I suggest following either camel case (sessionId) or snake case convention here (session_id)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your intention is to get sessionID -> you can use
request.session.session_key
If your intention is to get LOG_EVENTS_ID -> you can use
StateStore.get(Common.LOG_EVENTS_ID)

# Get the Redis connection
r = get_redis_connection("default")

key_pattern = f"logs:{session_id}*"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: This key creation / generation can be encapsulated in a function so that the push / retrieve from redis can follow the same logic. It'll be easier to maintain in case we wish to change this key later

Copy link
Contributor

@athul-rs athul-rs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall lgtm, left couple of comments. Please check

backend/logs_helper/log_service.py Outdated Show resolved Hide resolved
backend/account/authentication_controller.py Outdated Show resolved Hide resolved
@@ -259,6 +260,8 @@ def make_user_organization_display_name(self, user_name: str) -> str:
return self.auth_service.make_user_organization_display_name(user_name)

def user_logout(self, request: Request) -> Response:
session_id: str = request.COOKIES.get("sessionid")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your intention is to get sessionID -> you can use
request.session.session_key
If your intention is to get LOG_EVENTS_ID -> you can use
StateStore.get(Common.LOG_EVENTS_ID)


class LogService:
@staticmethod
def remove_logs_on_logout(session_id):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add arguments Types and function return type


if session_id:
# Get the Redis connection
r = get_redis_connection("default")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you re use CacheService from backend/utils/cache_service.py

There would have the cache functions. If not please add there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍

key_pattern = f"logs:{session_id}*"

# Retrieve keys matching the pattern and delete them
keys = r.keys(key_pattern)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cacheservice have a function clear_cache with key pattern. We can use that function here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it

@action(detail=False, methods=["get"])
def get_logs(self, request):
# Extract the session ID
session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thing the variable should renamed as log_event_id.
If we need real session_id here , please use request.session.session_key

"""Store log message in Redis."""
# Extract the session ID
logs_expiry = int(os.environ.get("LOGS_EXPIRATION_TIME_IN_SECOND", 3600))
session_id: str = StateStore.get(LogsHelperKeys.LOG_EVENTS_ID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like previous comments , change variable name


redis_key = f"logs:{session_id}:{timestamp}"

self.r.setex(redis_key, logs_expiry, log)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

insetad of this, can we use redis log? .. (I added the same comments somw where else too)

return Response({"data": sorted_logs}, status=status.HTTP_200_OK)

@action(detail=False, methods=["post"])
def store_log(self, request):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to know: Are we calling this method in every user logs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, We call this method every time a something logged from frontend.

@@ -137,3 +137,5 @@ ENABLE_LOG_HISTORY=True
LOG_HISTORY_CONSUMER_INTERVAL=30
# Maximum number of logs to insert in a single batch.
LOGS_BATCH_LIMIT=30
# Logs Expiry
LOGS_EXPIRATION_TIME_IN_SECOND=3600
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add same in to django settings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it

class LogsHelperViewSet(viewsets.ModelViewSet):
"""Viewset to handle all Tool Studio prompt related API logics."""

r = get_redis_connection("default")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this and use cacheservice

unstract/core/src/unstract/core/pubsub_helper.py Outdated Show resolved Hide resolved
@@ -4,16 +4,11 @@
from datetime import datetime, timezone
from typing import Any, Optional

import redis
from django_redis import get_redis_connection
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't do this. This pubsub_helper is core library package that we are using in worker. backend,etc.. Some of the services are not in django, and they don;t have the context of redis

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it 👍

cls.r.publish(channel, log_data)

# Check if the payload type is "LOG"
if payload["type"] == "LOG":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should n't be here. please move this in to consumer in backend.
Also, we have some changes in consumer too for design change of socket IO

@@ -4,16 +4,11 @@
from datetime import datetime, timezone
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package have some changes in another PR. that have design changes of socketio messages.

@tahierhussain tahierhussain marked this pull request as draft June 10, 2024 04:53
@tahierhussain tahierhussain marked this pull request as ready for review June 10, 2024 05:08
Copy link

filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_logs}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup\_skip}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_client\_init}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config\_without\_mount}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{worker/src/unstract/worker/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_run\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{9}}$$ $$\textcolor{#23d18b}{\tt{9}}$$

Copy link

sonarcloud bot commented Jun 20, 2024

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jagadeeswaran-zipstack remove this if unused

return Response({"data": sorted_logs}, status=status.HTTP_200_OK)

@action(detail=False, methods=["post"])
def store_log(self, request: HttpRequest) -> Response:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jagadeeswaran-zipstack why do we have this API? When / how will FE be calling this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're storing notifications from the frontend. For instance, when an adapter is created, we'll get a notification that says 'Added Successfully'. These messages are stored using this API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants