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

Communication: Add shortcut to private messages on usernames #10007

Merged

Conversation

PaRangger
Copy link
Contributor

@PaRangger PaRangger commented Dec 12, 2024

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the server coding and design guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I added pre-authorization annotations according to the guidelines and checked the course groups for all new REST Calls (security).
  • I documented the Java code using JavaDoc style.

Client

  • Important: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data (e.g. using paging).
  • I strictly followed the principle of data economy for all client-server REST calls.
  • I strictly followed the client coding and design guidelines.
  • I added multiple integration tests (Jest) related to the features (with a high test coverage), while following the test guidelines.
  • I documented the TypeScript code using JSDoc style.
  • I added multiple screenshots/screencasts of my UI changes.

Motivation and Context

Currently you the only option for you to directly create a one to one chat with another user is by clicking the create channel icon and searching for the user. This creates a lot of interactions and server calls, which could be avoided by simply providing an option to click usernames to jump to a private channel.

Description

I added the option to click on usernames in channels to directly jump to a one to one chat with the user. You should not be able to click your own username or the username of deleted users. Additionally, I added a api to create a one to one chat by user id for the native apps.

Steps for Testing

Prerequisites:

  • 2 Students/Tutors/Instructors
  • 1 Course with communication enabled
  1. Log in to Artemis with user 1
  2. Navigate to course
  3. Navigate to the communication tab
  4. Create a message
  5. Log out with user 1 and log in with user 2
  6. Go to the same channel
  7. Click the username of user 1
  8. You should be redirected to a one to one chat with user 1

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Review Progress

Code Review

  • Code Review 1

Manual Tests

  • Test 1
  • Test 2

Test Coverage

Client

Class/File Line Coverage Confirmation (assert/expect)
one-to-one-chat.service.ts 100%
metis-conversation.service.ts 85.1%
metis.service.ts 86.69%
post.component.ts 95.19%
posting-header.directive.ts 93.75%
posting.directive.ts 93.58%

Server

Class/File Line Coverage Confirmation (assert/expect)
OneToOneChatResource.java 100%

Screenshots

shortcut

Summary by CodeRabbit

Release Notes

  • New Features

    • Added functionality to initiate one-to-one chats using user IDs.
    • Enhanced interactivity in post and answer components with new event bindings for user interactions.
  • Bug Fixes

    • Improved handling of user reference clicks and username clicks across various components.
  • Documentation

    • Updated comments and documentation to reflect changes in method parameters and return types.
  • Tests

    • Added new test cases for one-to-one chat creation and user interaction scenarios.
    • Renamed existing tests for clarity and improved structure.
  • Chores

    • Cleaned up imports and updated test setups for better organization and maintainability.

@PaRangger PaRangger added feature server Pull requests that update Java code. (Added Automatically!) client Pull requests that update TypeScript code. (Added Automatically!) component:Communication communication Pull requests that affect the corresponding module labels Dec 12, 2024
@PaRangger PaRangger self-assigned this Dec 12, 2024
@github-actions github-actions bot added the tests label Dec 12, 2024
@PaRangger PaRangger temporarily deployed to artemis-test5.artemis.cit.tum.de December 13, 2024 11:11 — with GitHub Actions Inactive
@PaRangger PaRangger marked this pull request as ready for review December 13, 2024 11:22
@PaRangger PaRangger requested a review from a team as a code owner December 13, 2024 11:22
Copy link

coderabbitai bot commented Dec 13, 2024

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 pmd (7.8.0)
src/main/java/de/tum/cit/aet/artemis/communication/web/conversation/OneToOneChatResource.java

The following rules are missing or misspelled in your ruleset file category/vm/bestpractices.xml: BooleanInstantiation, DontImportJavaLang, DuplicateImports, EmptyFinallyBlock, EmptyIfStmt, EmptyInitializer, EmptyStatementBlock, EmptyStatementNotInLoop, EmptySwitchStatements, EmptySynchronizedBlock, EmptyTryBlock, EmptyWhileStmt, ExcessiveClassLength, ExcessiveMethodLength, ImportFromSamePackage, MissingBreakInSwitch, SimplifyBooleanAssertion. Please check your ruleset configuration.

src/test/java/de/tum/cit/aet/artemis/communication/OneToOneChatIntegrationTest.java

The following rules are missing or misspelled in your ruleset file category/vm/bestpractices.xml: BooleanInstantiation, DontImportJavaLang, DuplicateImports, EmptyFinallyBlock, EmptyIfStmt, EmptyInitializer, EmptyStatementBlock, EmptyStatementNotInLoop, EmptySwitchStatements, EmptySynchronizedBlock, EmptyTryBlock, EmptyWhileStmt, ExcessiveClassLength, ExcessiveMethodLength, ImportFromSamePackage, MissingBreakInSwitch, SimplifyBooleanAssertion. Please check your ruleset configuration.

Walkthrough

The pull request introduces several enhancements to the one-to-one chat functionality within the application. A new method for starting chats using user IDs has been added, alongside refactoring existing methods for better input validation and chat creation. The HTML components have been updated to improve user interactions, including new event bindings. Additionally, multiple test cases have been added or modified to ensure comprehensive coverage of the new functionalities and maintain existing logic integrity.

Changes

File Change Summary
src/main/java/de/tum/cit/aet/artemis/communication/web/conversation/OneToOneChatResource.java - Added method startOneToOneChat(Long courseId, Long userId)
- Refactored startOneToOneChat(List<String> otherChatParticipantLogins)
- Added private methods validateInputElseThrow(User requestingUser, Course course) and createOneToOneChat(User requestingUser, User userToBeNotified, Course course)
- Updated documentation comments and import statements.
src/main/webapp/app/shared/metis/answer-post/answer-post.component.html - Added output event (onUserNameClicked)
- Updated event handler for userReferenceClicked.
src/main/webapp/app/shared/metis/conversations/one-to-one-chat.service.ts - Added method createWithId(courseId: number, userIdOfChatPartner: number).
src/main/webapp/app/shared/metis/metis-conversation.service.ts - Added method createOneToOneChatWithId(userId: number).
src/main/webapp/app/shared/metis/metis.service.ts - Changed property visibility of course from private to public.
src/main/webapp/app/shared/metis/post/post.component.html - Added event binding (onUserNameClicked).
src/main/webapp/app/shared/metis/post/post.component.ts - Removed method onUserReferenceClicked(referencedUserLogin: string).
src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html - Added ngClass directive for clickable author's name.
src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html - Added ngClass directive for clickable author's name.
src/main/webapp/app/shared/metis/posting-header/posting-header.directive.ts - Added output property onUserNameClicked and method userNameClicked().
src/main/webapp/app/shared/metis/posting.directive.ts - Added methods onUserReferenceClicked(referencedUserLogin: string) and onUserNameClicked().
- Added properties oneToOneChatService and metisConversationService.
src/test/java/de/tum/cit/aet/artemis/communication/OneToOneChatIntegrationTest.java - Renamed existing test methods for clarity
- Added new test methods for user ID chat scenarios.
src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts - Added test case for createOneToOneChatWithId.
src/test/javascript/spec/component/overview/course-conversations/services/one-to-one-chat.service.spec.ts - Restructured tests and added new test cases for create and createWithId methods.
src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts - Updated imports and providers for enhanced testing.
src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts - Set course property in metisService for test context.
src/test/javascript/spec/directive/posting.directive.spec.ts - Enhanced test setup with new mock services and added test cases for user interactions.
src/test/javascript/spec/helpers/mocks/service/mock-metis-conversation.service.ts - Added methods to simulate one-to-one chat creation in the mock service.

Possibly related PRs

Suggested labels

ready to merge, small

Suggested reviewers

  • JohannesStoehr
  • HawKhiem
  • FelberMartin
  • asliayk
  • sachmii
  • krusche

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (12)
src/main/java/de/tum/cit/aet/artemis/communication/web/conversation/OneToOneChatResource.java (1)

125-140: Consider moving business logic to the service layer

While the extraction of validateInputElseThrow and createOneToOneChat methods improves organization, consider moving these methods to a service class to adhere to the single responsibility principle and keep the controller thin.

src/main/webapp/app/shared/metis/conversations/one-to-one-chat.service.ts (1)

23-27: Consider passing an empty object instead of null in POST request

In the createWithId method, passing {} instead of null as the body of the POST request improves clarity and adheres to HttpClient conventions.

Apply this diff to make the change:

return this.http
-    .post<OneToOneChatDTO>(`${this.resourceUrl}${courseId}/one-to-one-chats/${userIdOfChatPartner}`, null, { observe: 'response' })
+    .post<OneToOneChatDTO>(`${this.resourceUrl}${courseId}/one-to-one-chats/${userIdOfChatPartner}`, {}, { observe: 'response' })
    .pipe(map(this.conversationService.convertDateFromServer));
src/test/javascript/spec/helpers/mocks/service/mock-metis-conversation.service.ts (1)

43-50: Ensure mock methods' return types match the actual service

To maintain consistency and type safety, consider specifying return types that match the actual MetisConversationService methods, such as Observable<ConversationDTO>.

Apply this diff to adjust the return types:

 createOneToOneChatWithId = (userId: number): Observable<never> => {
-    return EMPTY;
+    return of(new ConversationDTO());
 };

 createOneToOneChat = (userId: number): Observable<never> => {
-    return EMPTY;
+    return of(new ConversationDTO());
 };
src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html (1)

18-18: Consider extracting common username click functionality.

The implementation is correct but identical to answer-post-header.component.html. Consider extracting this into a shared component or directive to promote DRY principles.

You could create a shared directive:

@Directive({
  selector: '[jhiClickableUsername]'
})
export class ClickableUsernameDirective {
  @Input() isAuthorOfPosting: boolean;
  @Input() authorRole: any;
  @Output() userNameClicked = new EventEmitter<void>();
}

Then use it in both components:

-<span class="fw-semibold" [ngClass]="{ clickable: !isAuthorOfPosting && posting.authorRole }" (click)="userNameClicked()">
+<span class="fw-semibold" jhiClickableUsername [isAuthorOfPosting]="isAuthorOfPosting" [authorRole]="posting.authorRole" (userNameClicked)="userNameClicked()">
src/main/webapp/app/shared/metis/posting-header/posting-header.directive.ts (1)

105-111: Consider adding return type and improving error handling.

While the implementation is correct, it could benefit from explicit typing and error handling.

-    protected userNameClicked() {
+    protected userNameClicked(): void {
         if (this.isAuthorOfPosting || !this.posting.authorRole) {
+            console.debug('Ignoring click on own username or deleted user');
             return;
         }
 
         this.onUserNameClicked.emit();
     }
src/main/webapp/app/shared/metis/posting.directive.ts (2)

35-39: Use private access modifier for injected services.

The services should be marked as private since they're only used within the class.

-    protected oneToOneChatService = inject(OneToOneChatService);
-    protected metisConversationService = inject(MetisConversationService);
-    protected metisService = inject(MetisService);
-    protected changeDetector = inject(ChangeDetectorRef);
-    protected router = inject(Router);
+    private readonly oneToOneChatService = inject(OneToOneChatService);
+    private readonly metisConversationService = inject(MetisConversationService);
+    private readonly metisService = inject(MetisService);
+    private readonly changeDetector = inject(ChangeDetectorRef);
+    private readonly router = inject(Router);

7-10: Consider organizing imports.

Group related imports together for better readability.

 import { Posting } from 'app/entities/metis/posting.model';
 import { ChangeDetectorRef, Directive, Input, OnDestroy, OnInit, inject } from '@angular/core';
+import { Router } from '@angular/router';
+
 import { MetisService } from 'app/shared/metis/metis.service';
 import { DisplayPriority } from 'app/shared/metis/metis.util';
-import { faBookmark } from '@fortawesome/free-solid-svg-icons';
-import { faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
 import { isMessagingEnabled } from 'app/entities/course.model';
 import { OneToOneChatService } from 'app/shared/metis/conversations/one-to-one-chat.service';
 import { MetisConversationService } from 'app/shared/metis/metis-conversation.service';
-import { Router } from '@angular/router';
+
+import { faBookmark } from '@fortawesome/free-solid-svg-icons';
+import { faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts (1)

272-296: Consider adding error case testing

The test suite would benefit from additional test cases that verify error handling scenarios, such as:

  • Invalid user ID
  • Network errors
  • Server errors

Example test case:

it('should handle errors when creating one to one chat with id', () => {
    return new Promise((done) => {
        metisConversationService.setUpConversationService(course).subscribe({
            complete: () => {
                const error = new HttpErrorResponse({ status: 404, statusText: 'Not Found' });
                const createOneToOneChatSpy = jest.spyOn(oneToOneChatService, 'createWithId')
                    .mockReturnValue(throwError(() => error));
                const alertSpy = jest.spyOn(alertService, 'addAlert');

                metisConversationService.createOneToOneChatWithId(999).subscribe({
                    error: () => {
                        expect(createOneToOneChatSpy).toHaveBeenCalledWith(course.id, 999);
                        expect(alertSpy).toHaveBeenCalled();
                        done({});
                    }
                });
            }
        });
    });
});
src/main/webapp/app/shared/metis/metis.service.ts (4)

Line range hint 1-824: Consider splitting the service into smaller, focused services.

The MetisService has grown quite large and handles multiple responsibilities (posts, reactions, websockets, routing). This violates the Single Responsibility Principle and makes the code harder to maintain and test. Consider splitting it into:

  • MetisPostService: Handle post-related operations
  • MetisWebSocketService: Handle websocket communications
  • MetisRoutingService: Handle routing logic

Line range hint 1-824: Prevent memory leaks by completing subjects and managing subscriptions.

Several potential memory leak points were identified:

  1. The posts$, tags$, and totalNumberOfPosts$ subjects are never completed
  2. Some method subscriptions (e.g., in deletePost, deleteAnswerPost) don't unsubscribe

Consider implementing the following:

  1. Complete the subjects in ngOnDestroy:
 ngOnDestroy(): void {
     if (this.subscriptionChannel) {
         this.jhiWebsocketService.unsubscribe(this.subscriptionChannel);
     }
     this.courseWideTopicSubscription.unsubscribe();
+    this.posts$.complete();
+    this.tags$.complete();
+    this.totalNumberOfPosts$.complete();
 }
  1. Use takeUntil or store subscriptions for cleanup:
 deletePost(post: Post): void {
-    this.postService
+    const subscription = this.postService
         .delete(this.courseId, post)
         .pipe(
             tap(() => {
                 // ... existing code ...
             }),
         )
         .subscribe();
+    this.subscriptions.push(subscription);
 }

Line range hint 1-824: Improve error handling across service methods.

The service lacks comprehensive error handling for HTTP and WebSocket operations. This could lead to silent failures and poor user experience.

Consider implementing error handling:

  1. Add error handling to HTTP operations:
 createPost(post: Post): Observable<Post> {
     return this.postService.create(this.courseId, post).pipe(
         map((res: HttpResponse<Post>) => res.body!),
         tap((createdPost: Post) => {
             // ... existing code ...
         }),
+        catchError(error => {
+            this.notificationService.error('Error creating post');
+            return throwError(() => error);
+        })
     );
 }
  1. Add WebSocket error handling:
 private handleNewOrUpdatedMessage = (postDTO: MetisPostDTO): void => {
+    try {
         // ... existing code ...
+    } catch (error) {
+        console.error('Error processing WebSocket message:', error);
+        this.notificationService.error('Error processing message');
+    }
 };

Line range hint 1-824: Enhance security with consistent permission checks and input validation.

Several security considerations need to be addressed:

  1. Ensure consistent permission checks before operations
  2. Add input validation for user-provided data
  3. Protect against XSS in post content

Consider implementing these security enhancements:

  1. Create a permission check decorator:
function requirePermission(minRole: 'TUTOR' | 'INSTRUCTOR') {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            if (minRole === 'INSTRUCTOR' && !this.metisUserIsAtLeastInstructorInCourse()) {
                throw new Error('Insufficient permissions');
            }
            if (minRole === 'TUTOR' && !this.metisUserIsAtLeastTutorInCourse()) {
                throw new Error('Insufficient permissions');
            }
            return originalMethod.apply(this, args);
        };
        return descriptor;
    };
}
  1. Add input validation:
 createPost(post: Post): Observable<Post> {
+    if (!post.content || post.content.trim().length === 0) {
+        return throwError(() => new Error('Post content is required'));
+    }
     return this.postService.create(this.courseId, post).pipe(
         // ... existing code ...
     );
 }
  1. Sanitize post content:
 createPost(post: Post): Observable<Post> {
+    post.content = this.sanitizer.sanitize(SecurityContext.HTML, post.content);
     return this.postService.create(this.courseId, post).pipe(
         // ... existing code ...
     );
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fafe68c and aa1d103.

📒 Files selected for processing (18)
  • src/main/java/de/tum/cit/aet/artemis/communication/web/conversation/OneToOneChatResource.java (3 hunks)
  • src/main/webapp/app/shared/metis/answer-post/answer-post.component.html (2 hunks)
  • src/main/webapp/app/shared/metis/conversations/one-to-one-chat.service.ts (1 hunks)
  • src/main/webapp/app/shared/metis/metis-conversation.service.ts (1 hunks)
  • src/main/webapp/app/shared/metis/metis.service.ts (1 hunks)
  • src/main/webapp/app/shared/metis/post/post.component.html (1 hunks)
  • src/main/webapp/app/shared/metis/post/post.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html (1 hunks)
  • src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html (1 hunks)
  • src/main/webapp/app/shared/metis/posting-header/posting-header.directive.ts (3 hunks)
  • src/main/webapp/app/shared/metis/posting.directive.ts (3 hunks)
  • src/test/java/de/tum/cit/aet/artemis/communication/OneToOneChatIntegrationTest.java (6 hunks)
  • src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts (1 hunks)
  • src/test/javascript/spec/component/overview/course-conversations/services/one-to-one-chat.service.spec.ts (1 hunks)
  • src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts (2 hunks)
  • src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts (1 hunks)
  • src/test/javascript/spec/directive/posting.directive.spec.ts (3 hunks)
  • src/test/javascript/spec/helpers/mocks/service/mock-metis-conversation.service.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (18)
src/main/webapp/app/shared/metis/post/post.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/shared/metis/conversations/one-to-one-chat.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/helpers/mocks/service/mock-metis-conversation.service.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/metis/metis.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/post/post.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/metis-conversation.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/metis/answer-post/answer-post.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/shared/metis/posting-header/posting-header.directive.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting.directive.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/overview/course-conversations/services/one-to-one-chat.service.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/directive/posting.directive.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/java/de/tum/cit/aet/artemis/communication/web/conversation/OneToOneChatResource.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/test/java/de/tum/cit/aet/artemis/communication/OneToOneChatIntegrationTest.java (1)

Pattern src/test/java/**/*.java: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true

🔇 Additional comments (25)
src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts (3)

21-23: LGTM: Import changes align with testing requirements

The new imports properly support HTTP testing, storage services, and conversation services needed for the username click feature. The organization follows the project's conventions.

Also applies to: 27-32


59-60: LGTM: Provider configuration follows testing best practices

The HTTP client and service mocks are properly configured following the project's testing guidelines.

Also applies to: 64-65


59-65: Add test coverage for username click functionality

Given that this component will now support clicking usernames to start private messages (per PR objectives), we should add test cases to verify:

  1. Username click event handling
  2. Integration with MetisConversationService
  3. Error scenarios (e.g., clicking own username, deleted users)

Let's verify if there are any existing tests for this functionality:

src/main/java/de/tum/cit/aet/artemis/communication/web/conversation/OneToOneChatResource.java (4)

28-29: Necessary imports added

The additions of Course and User imports are required for the new functionalities implemented.


81-81: Good practice of input validation

Refactoring the input validation into the validateInputElseThrow method enhances code readability and reusability.


95-96: Improved code maintainability by extracting chat creation logic

By moving the chat creation logic into the createOneToOneChat method, you've reduced code duplication and improved maintainability.


98-117: New endpoint for initiating chats using user IDs

The addition of the startOneToOneChat method with user ID enhances the API, providing a more efficient way to initiate chats.

src/test/javascript/spec/directive/posting.directive.spec.ts (4)

75-84: Proper use of mocks and providers in test setup

The inclusion of mock services and providers ensures that the tests are isolated and dependencies are correctly mocked.


177-184: Correct handling of undefined author in tests

The test effectively checks the scenario when author is undefined and uses the appropriate Jest matcher not.toHaveBeenCalled().


249-251: Using appropriate Jest matcher for boolean assertion

The test correctly uses toBeTrue() to assert boolean values, adhering to the testing guidelines.


301-310: Accurate implementation of timer decrement in tests

The use of jest.advanceTimersByTime() and checking the decremented value demonstrates precise control over timer functions in tests.

src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html (1)

18-18: LGTM! Username click functionality implemented correctly.

The implementation correctly prevents clicking own username and handles user roles appropriately. The conditional class application aligns with the PR objectives.

Let's verify the CSS class implementation:

✅ Verification successful

CSS implementation verified, username click functionality is properly supported

The implementation is correct and well-supported:

  • The .clickable class is properly defined in global.scss with cursor: pointer styling
  • The same click handler pattern is consistently used in both post header components
  • The conditional class application prevents clicking own username and respects user roles
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the clickable class definition exists in CSS files
# and check for any similar existing implementations

# Search for clickable class definition
rg -g '*.{css,scss}' 'clickable\s*{'

# Search for any similar click handlers on usernames
rg -g '*.html' 'userNameClicked|username.*click'

Length of output: 907


Script:

#!/bin/bash
# Fix the regex and search for clickable class definition
rg -g '*.{css,scss}' '\.clickable'

# Search for the component implementation to verify userNameClicked handler
ast-grep --pattern 'userNameClicked() {
  $$$
}'

Length of output: 820


Script:

#!/bin/bash
# Search for the component implementation with correct TypeScript syntax
ast-grep --pattern 'class $_ {
  $$$
  userNameClicked() {
    $$$
  }
  $$$
}'

# Check the clickable class definition in global.scss
cat src/main/webapp/content/scss/global.scss | grep -A 5 '\.clickable'

Length of output: 358

src/test/javascript/spec/component/overview/course-conversations/services/one-to-one-chat.service.spec.ts (1)

15-17: LGTM! Mock setup is clean and focused.

The ConversationService mock is properly implemented with the necessary convertDateFromServer method.

src/main/webapp/app/shared/metis/posting-header/posting-header.directive.ts (1)

22-22: LGTM! Modern Angular syntax used.

The output property uses the new-style Angular output syntax, which is more type-safe and performant.

src/main/webapp/app/shared/metis/answer-post/answer-post.component.html (2)

27-27: LGTM! Event binding follows Angular conventions.

The event binding is correctly implemented and follows the component communication pattern.


43-43: LGTM! Event handler updated correctly.

The event handler has been properly updated to use the new method.

src/main/webapp/app/shared/metis/post/post.component.ts (1)

26-26: LGTM! Verify chat functionality relocation.

The removal of chat-related functionality and addition of the messaging check import aligns with single responsibility principle.

✅ Verification successful

The search results show that the chat functionality is still present and properly organized in the codebase:

  1. OneToOneChatService is defined in conversations/one-to-one-chat.service.ts
  2. The chat functionality is used through PostingDirective which is the base class for post components
  3. onUserReferenceClicked is still being used in both post and answer-post components
  4. The functionality is properly integrated with MetisConversationService

Let me verify the specific changes in the post component to ensure the refactoring was done correctly:


Let me try a different approach to verify the specific changes:


LGTM! Chat functionality is properly organized and maintained

The chat functionality has not been removed but rather is well-organized:

  • The PostComponent inherits from PostingDirective which contains the chat-related functionality
  • The onUserReferenceClicked method is implemented in the base class and handles both communication page and regular page scenarios
  • The new import isCommunicationEnabled is used to check if messaging is enabled before attempting to use chat features
  • The functionality is properly integrated with OneToOneChatService and MetisConversationService
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the chat functionality has been properly relocated
rg -A 5 "onUserReferenceClicked|OneToOneChatService" src/main/webapp/

Length of output: 6301


Script:

#!/bin/bash
# Check the implementation of post component and its parent class
ast-grep --pattern 'export class PostComponent {
  $$$
}'

ast-grep --pattern 'export abstract class PostingDirective {
  $$$
  onUserReferenceClicked($_) {
    $$$
  }
  $$$
}'

Length of output: 304


Script:

#!/bin/bash
# Check the specific changes in post component and its parent class
rg -l "class PostComponent" src/main/webapp/
rg -l "class PostingDirective" src/main/webapp/

# Then get their content
cat $(rg -l "class PostComponent" src/main/webapp/) || true
cat $(rg -l "class PostingDirective" src/main/webapp/) || true

Length of output: 16831

src/main/webapp/app/shared/metis/post/post.component.html (1)

27-27: LGTM! Verify event handler implementation.

The addition of onUserNameClicked event binding enables the core feature of clicking usernames to start chats.

✅ Verification successful

LGTM! Event handler is properly implemented and tested

The onUserNameClicked event handler is well implemented in the posting.directive.ts with proper checks and functionality:

  • Guards against undefined author
  • Checks if messaging is enabled
  • Creates one-to-one chat based on the author ID
  • Comprehensive test coverage including edge cases and success scenarios
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the event handler implementation
ast-grep --pattern 'onUserNameClicked($$$)' src/main/webapp/

Length of output: 62


Script:

#!/bin/bash
# Search for TypeScript/JavaScript files containing onUserNameClicked
rg -t ts -t js "onUserNameClicked" -A 5

# Search for the component class definition containing onUserNameClicked
ast-grep --pattern 'export class $_ {
  $$$
  onUserNameClicked($$$) {
    $$$
  }
  $$$
}'

Length of output: 5796

src/test/java/de/tum/cit/aet/artemis/communication/OneToOneChatIntegrationTest.java (4)

Line range hint 52-149: LGTM! Test method names follow BDD conventions.

The renamed test methods clearly describe their behavior and expected outcomes, following the "should" pattern.


153-163: LGTM! Comprehensive test coverage for new chat creation.

Test properly verifies successful chat creation using user IDs and checks participant count.


165-179: LGTM! Edge case handling for existing chats.

Test ensures that attempting to create a chat with the same user returns the existing chat instead of creating a duplicate.


181-206: LGTM! Error cases are well covered.

Tests handle:

  • Unknown user IDs (404)
  • Disabled messaging (403)
  • Unauthorized access (403)
src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts (1)

96-96: LGTM: Proper test setup initialization

The addition of metisService.course = metisCourse in the beforeEach block ensures proper test context by initializing the required course information.

src/main/webapp/app/shared/metis/metis-conversation.service.ts (1)

213-214: LGTM: Well-structured method addition

The new createOneToOneChatWithId method follows the established patterns:

  • Consistent with existing chat creation methods
  • Proper use of arrow function syntax
  • Correct service delegation
src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts (1)

272-296: LGTM: Comprehensive test coverage for new method

The test case thoroughly verifies the createOneToOneChatWithId functionality:

  • Proper service mocking
  • Verification of method calls and parameters
  • Assertion of expected outcomes

Copy link

@HawKhiem HawKhiem left a comment

Choose a reason for hiding this comment

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

Tested on TS5. Works as described

Copy link

@sawys777 sawys777 left a comment

Choose a reason for hiding this comment

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

Tested on TS5, the shortcut works as expected.

Copy link
Contributor

@asliayk asliayk left a comment

Choose a reason for hiding this comment

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

tested on ts2 and works as expected

@PaRangger PaRangger requested a review from a team as a code owner December 20, 2024 10:06
@krusche krusche added this to the 7.8.0 milestone Dec 20, 2024
@krusche krusche changed the title Communication: Create shortcut to private messages by clicking usernames in channel Communication: Add shortcut to private messages on usernames Dec 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Pull requests that update TypeScript code. (Added Automatically!) communication Pull requests that affect the corresponding module component:Communication feature ready to merge server Pull requests that update Java code. (Added Automatically!) tests
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

7 participants