Skip to content

Commit 12c70da

Browse files
committed
✨ feat(frontend): Display predicted spam label in post
1 parent ebd40e3 commit 12c70da

File tree

9 files changed

+90
-18
lines changed

9 files changed

+90
-18
lines changed

chart/templates/microblog-service.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ spec:
6868
value: {{ quote .Values.microblogService.deployment.container.env.USER_AUTH_SERVICE_ADDRESS }}
6969
- name: RAG_SERVICE_ADDRESS
7070
value: {{ quote .Values.microblogService.deployment.container.env.RAG_SERVICE_ADDRESS }}
71+
- name: RAG_SERVICE_PORT
72+
value: {{ quote .Values.microblogService.deployment.container.env.RAG_SERVICE_PORT }}
7173
- name: OPENTRACING_JAEGER_ENABLED
7274
value: {{ quote .Values.microblogService.deployment.container.env.OPENTRACING_JAEGER_ENABLED }}
7375
- name: JAEGER_SERVICE_NAME

chart/values.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ microblogService:
279279
OPENTRACING_JAEGER_ENABLED: false
280280
REDIS_SERVICE_ADDRESS: unguard-redis
281281
USER_AUTH_SERVICE_ADDRESS: unguard-user-auth-service
282-
RAG_SERVICE_ADDRESS: unguard-rag-service:8000
282+
RAG_SERVICE_ADDRESS: unguard-rag-service
283+
RAG_SERVICE_PORT: 8000
283284

284285
# Status Service
285286
statusService:

src/frontend-nextjs/app/post/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ function SinglePost() {
3333
postId={postData.postId}
3434
timestamp={postData.timestamp}
3535
username={postData.username}
36+
isSpamPredictedLabel={postData.isSpamPredictedLabel}
3637
/>
3738
)}
3839
</div>

src/frontend-nextjs/components/Timeline/Post.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import { BASE_PATH } from '@/constants';
1010
import { useCheckLogin } from '@/hooks/queries/useCheckLogin';
1111
import { LikeButton } from '@/components/Timeline/LikeButton';
1212
import { ErrorCard } from '@/components/ErrorCard';
13+
import { PostSpamPrediction } from '@/components/Timeline/PostSpamPrediction';
1314

1415
export interface PostProps {
1516
username: string;
1617
timestamp: string;
1718
body: string;
1819
imageUrl?: string;
1920
postId: string;
21+
isSpamPredictedLabel?: boolean;
2022
}
2123

2224
export function Post(props: PostProps) {
@@ -69,7 +71,11 @@ export function Post(props: PostProps) {
6971
)}
7072
<p>{props.body}</p>
7173
</CardBody>
72-
<CardFooter className='gap-3 justify-end px-3'>
74+
<CardFooter className='gap-3 justify-between px-3'>
75+
<div className='flex items-center'>
76+
<PostSpamPrediction isSpamPredictedLabel={props.isSpamPredictedLabel} />
77+
</div>
78+
7379
{isLoggedIn && (
7480
<div className='flex gap-1'>
7581
<ErrorBoundary fallbackRender={(props) => <ErrorCard message={props.error.message} />}>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import {Alert} from "@heroui/react";
3+
4+
export interface PostSpamPredictionProps {
5+
isSpamPredictedLabel?: boolean | null;
6+
}
7+
8+
9+
10+
export function PostSpamPrediction(props: Readonly<PostSpamPredictionProps>) {
11+
console.log("PostSpamPrediction props:", props);
12+
if (props.isSpamPredictedLabel == null) return null;
13+
14+
15+
if (props.isSpamPredictedLabel) {
16+
const color='danger'
17+
return (
18+
<div key={color} className='w-full flex items-center my-3'>
19+
<div>
20+
<Alert color={color} title={'Potential Spam Detected'} />
21+
</div>
22+
</div>
23+
);
24+
}
25+
26+
const color = 'primary'
27+
return (
28+
<div key={color} className='w-full flex items-center my-3'>
29+
<div>
30+
<Alert color={color} title={'No Spam Detected'} />
31+
</div>
32+
</div>
33+
);
34+
}

src/frontend-nextjs/components/Timeline/Timeline.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use client';
22
import { Card, Spacer, Spinner } from '@heroui/react';
33

4-
import { Post } from '@/components/Timeline/Post';
5-
import { PostProps } from '@/components/Timeline/Post';
4+
import { Post, PostProps } from '@/components/Timeline/Post';
65

76
interface TimelineProps {
87
posts: PostProps[] | undefined;
@@ -31,6 +30,7 @@ export function Timeline({ posts, isLoading }: TimelineProps) {
3130
postId={post.postId}
3231
timestamp={post.timestamp}
3332
username={post.username}
33+
isSpamPredictedLabel={post.isSpamPredictedLabel}
3434
/>
3535
<Spacer y={4} />
3636
</div>

src/microblog-service/src/main/java/org/dynatrace/microblog/MicroblogController.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@
2323
import org.dynatrace.microblog.dto.PostId;
2424
import org.dynatrace.microblog.dto.SerializedPost;
2525
import org.dynatrace.microblog.dto.User;
26-
import org.dynatrace.microblog.exceptions.FollowYourselfException;
27-
import org.dynatrace.microblog.exceptions.InvalidJwtException;
28-
import org.dynatrace.microblog.exceptions.InvalidUserException;
29-
import org.dynatrace.microblog.exceptions.NotLoggedInException;
30-
import org.dynatrace.microblog.exceptions.UserNotFoundException;
26+
import org.dynatrace.microblog.exceptions.*;
3127
import org.dynatrace.microblog.form.PostForm;
3228
import org.dynatrace.microblog.ragservice.RAGServiceClient;
3329
import org.dynatrace.microblog.redis.RedisClient;
@@ -73,6 +69,7 @@ public MicroblogController(Tracer tracer, PostSerializer postSerializer) {
7369
String redisServiceAddress;
7470
String userAuthServiceAddress;
7571
String ragServiceAddress;
72+
String ragServicePort;
7673
if (System.getenv("REDIS_SERVICE_ADDRESS") != null) {
7774
redisServiceAddress = System.getenv("REDIS_SERVICE_ADDRESS");
7875
logger.info("REDIS_SERVICE_ADDRESS set to {}", redisServiceAddress);
@@ -93,13 +90,20 @@ public MicroblogController(Tracer tracer, PostSerializer postSerializer) {
9390
ragServiceAddress = System.getenv("RAG_SERVICE_ADDRESS");
9491
logger.info("RAG_SERVICE_ADDRESS set to {}", ragServiceAddress);
9592
} else {
96-
ragServiceAddress = "localhost:8000";
97-
logger.warn("No RAG_SERVICE_ADDRESS environment variable defined, falling back to localhost:8000.");
93+
ragServiceAddress = "localhost";
94+
logger.warn("No RAG_SERVICE_ADDRESS environment variable defined, falling back to localhost.");
95+
}
96+
if (System.getenv("RAG_SERVICE_PORT") != null) {
97+
ragServicePort = System.getenv("RAG_SERVICE_PORT");
98+
logger.info("RAG_SERVICE_PORT set to {}", ragServicePort);
99+
} else {
100+
ragServicePort = "8000";
101+
logger.warn("No RAG_SERVICE_PORT environment variable defined, falling back to 8000.");
98102
}
99103

100104
this.userAuthServiceClient = new UserAuthServiceClient(userAuthServiceAddress);
101105
this.redisClient = new RedisClient(redisServiceAddress, this.userAuthServiceClient, tracer);
102-
this.ragServiceClient = new RAGServiceClient(ragServiceAddress);
106+
this.ragServiceClient = new RAGServiceClient(ragServiceAddress, ragServicePort);
103107
this.postSerializer = postSerializer;
104108
}
105109

@@ -193,9 +197,15 @@ public PostId post(@RequestBody PostForm postForm, @CookieValue(value = "jwt", r
193197
CompletableFuture.runAsync(() -> {
194198
try {
195199
String spamClassificationResult = ragServiceClient.getSpamClassification(postForm.getContent());
196-
logger.info("RAG spam classification result for post with ID {}: {}", postId, spamClassificationResult);
197-
boolean isSpam = spamClassificationResult.trim().equalsIgnoreCase("spam");
198-
redisClient.setSpamPredictedLabel(postId, isSpam);
200+
if(spamClassificationResult == null || spamClassificationResult.trim().isEmpty()) {
201+
throw new InvalidSpamPredictionException("RAG spam classification result is null or empty for post with ID " + postId);
202+
} else if (spamClassificationResult.trim().equalsIgnoreCase("not_spam")) {
203+
redisClient.setSpamPredictedLabel(postId, false);
204+
} else if (spamClassificationResult.trim().equalsIgnoreCase("spam")) {
205+
redisClient.setSpamPredictedLabel(postId, true);
206+
} else {
207+
throw new InvalidSpamPredictionException("RAG spam classification result is invalid for post with ID " + postId + ": " + spamClassificationResult);
208+
}
199209
} catch (Exception e) {
200210
logger.warn("RAG spam classification failed for post with ID {}", postId, e);
201211
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.dynatrace.microblog.exceptions;
2+
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.web.bind.annotation.ResponseStatus;
5+
6+
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Invalid spam prediction")
7+
public class InvalidSpamPredictionException extends Exception {
8+
public InvalidSpamPredictionException(String message) {
9+
super(message);
10+
}
11+
}

src/microblog-service/src/main/java/org/dynatrace/microblog/ragservice/RAGServiceClient.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ public class RAGServiceClient {
2121
private final Logger logger = LoggerFactory.getLogger(org.dynatrace.microblog.ragservice.RAGServiceClient.class);
2222

2323
private final String ragServiceHost;
24+
private final int ragServicePort;
2425

25-
public RAGServiceClient(String ragServiceHost) {
26+
public RAGServiceClient(String ragServiceHost, String ragsServicePort) {
2627
this.ragServiceHost = ragServiceHost;
28+
this.ragServicePort = Integer.parseInt(ragsServicePort);
2729

2830
TracingInterceptor tracingInterceptor = new TracingInterceptor(
2931
GlobalTracer.get(),
@@ -47,10 +49,15 @@ public String getSpamClassification(String postText) throws InvalidJwtException,
4749
RequestBody body = RequestBody.create(
4850
MediaType.parse("application/json"), jsonRequest);
4951

50-
String requestUrl = "http://" + this.ragServiceHost + "/classifyPost";
52+
HttpUrl url = new HttpUrl.Builder()
53+
.scheme("http")
54+
.host(this.ragServiceHost)
55+
.port(this.ragServicePort)
56+
.addPathSegment("classifyPost")
57+
.build();
5158

5259
Request request = new Request.Builder()
53-
.url(requestUrl)
60+
.url(url)
5461
.post(body)
5562
.build();
5663

0 commit comments

Comments
 (0)