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

Development: Update LTI components to use Angular 18 practices #9908

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from

Conversation

raffifasaro
Copy link
Contributor

@raffifasaro raffifasaro commented Nov 29, 2024

Checklist

General

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.
  • Following the theming guidelines, I specified colors only in the theming variable files and checked that the changes look consistent in both the light and the dark theme.
  • I added multiple integration tests (Jest) related to the features (with a high test coverage), while following the test guidelines.
  • I added authorities to all new routes and checked the course groups for displaying navigation elements (links, buttons).
  • I documented the TypeScript code using JSDoc style.
  • I added multiple screenshots/screencasts of my UI changes.
  • I translated all newly inserted strings into English and German.

Motivation and Context

Migrating all LTI components to use Angular 18 practices

Description

Migrated LTI components in regard to standalone, input and signals
Adjusted tests to work with migrated components

Steps for Testing

Prerequisites:

  • Access to Moodle
  • Testserver 1
  1. Make sure you are logged in to Artemis with a test user (artemis_test_user_{1-5})
  2. Navigate to Moodle and login with the same test user (artemis_test_user_{1-5}, same PW as for the Artemis Test Server)
  3. Go to My Courses and open the course TS1 - Artemis Feature Demo Course
  4. Click on one of the exercise
  5. Artemis should open in an iFrame and display the exercise
    (If Moodle throws an error in the iFrame, just refresh the page)

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

Performance Review

  • I (as a reviewer) confirm that the client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance even for very large courses with more than 2000 students.
  • I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Test Coverage

Screenshots

Summary by CodeRabbit

Release Notes

  • New Features

    • Several components have been updated to be standalone, enhancing modularity and reusability.
    • New imports added to components allow for improved functionality, including translation support.
  • Bug Fixes

    • No bug fixes were included in this release.
  • Documentation

    • No new documentation was provided in this release.
  • Chores

    • Improved the structure and readability of module imports and declarations across multiple components.

@raffifasaro raffifasaro requested a review from a team as a code owner November 29, 2024 16:24
@github-actions github-actions bot added the client Pull requests that update TypeScript code. (Added Automatically!) label Nov 29, 2024
@raffifasaro raffifasaro marked this pull request as draft November 29, 2024 16:24
@raffifasaro raffifasaro changed the title DeLTI Client Migration Development: Update LTI components to use Angular 18 practices Nov 29, 2024
Copy link

coderabbitai bot commented Nov 29, 2024

Walkthrough

The changes in this pull request involve updating multiple components within the LTI module of the Angular application to be standalone components. This is achieved by adding the standalone: true property to the @Component decorator for each component. Additionally, various components now include updated imports arrays to incorporate new dependencies, enhancing their modularity and reusability. The overall logic and functionality of the components remain unchanged, focusing primarily on structural improvements.

Changes

File Path Change Summary
src/main/webapp/app/lti/lti-course-card.component.ts Updated @Component to include standalone: true and added imports: [RouterLink, NgStyle, ArtemisSharedModule]. Removed OnChanges lifecycle hook and changed @Input() to input() for course.
src/main/webapp/app/lti/lti.module.ts Reformatted imports and declarations arrays in @NgModule for improved readability; no functional changes.
src/main/webapp/app/lti/lti13-deep-linking.component.ts Updated @Component to include standalone: true and added imports: [ArtemisSharedModule, TranslateDirective, ArtemisSharedComponentModule, ArtemisSharedCommonModule, FaIconComponent, FormsModule]. Removed constructor in favor of property injection.
src/main/webapp/app/lti/lti13-dynamic-registration.component.ts Updated @Component to include standalone: true and added imports: [TranslateDirective]. Removed constructor in favor of property injection.
src/main/webapp/app/lti/lti13-exercise-launch.component.ts Updated @Component to include standalone: true and added imports: [TranslateDirective, CommonModule]. Removed constructor in favor of property injection.
src/main/webapp/app/lti/lti13-select-content.component.ts Updated @Component to include standalone: true and added imports: [TranslateDirective, FormsModule, ArtemisSharedPipesModule]. Removed constructor in favor of property injection.
src/main/webapp/app/lti/lti13-select-course.component.ts Updated @Component to include standalone: true and added imports: [LtiCourseCardComponent, TranslateDirective]. Removed constructor in favor of property injection.
src/main/webapp/app/admin/lti-configuration/edit-lti-configuration.component.ts Removed constructor and transitioned to property injection for services.
src/main/webapp/app/admin/lti-configuration/lti-configuration.component.ts Removed constructor and transitioned to property injection for services.
src/main/webapp/app/admin/lti-configuration/lti-configuration.service.ts Removed constructor and transitioned to property injection for HttpClient.
src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.ts Removed constructor and transitioned to property injection for services.
src/main/webapp/app/course/manage/course-lti-configuration/edit-course-lti-configuration.component.ts Removed constructor and transitioned to property injection for services.
src/main/webapp/app/lti/lti-course-card.component.html Updated template to access course object via function call instead of direct property access.
src/main/webapp/app/overview/exercise-details/lti-initializer-modal.component.ts Removed constructor and transitioned to property injection for services.
src/main/webapp/app/overview/exercise-details/lti-initializer.component.ts Removed constructor and transitioned to property injection for services.

Possibly related PRs

Suggested labels

refactoring, ready for review, small, component:LTI

Suggested reviewers

  • JohannesWt
  • florian-glombik
  • az108
  • BBesrour
  • HawKhiem
  • egekurt123
  • MaximilianAnzinger

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 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: 2

🧹 Outside diff range and nitpick comments (14)
src/main/webapp/app/lti/lti13-select-course.component.ts (1)

Line range hint 24-39: Implement proper subscription management and consistent async patterns.

Several improvements can enhance this component:

  1. Implement OnDestroy to clean up subscriptions
  2. Use consistent async patterns
  3. Add proper error typing

Consider applying these changes:

-export class LtiCoursesComponent implements OnInit {
+export class LtiCoursesComponent implements OnInit, OnDestroy {
     public courses: OnlineCourseDtoModel[];
+    private destroy$ = new Subject<void>();
 
     constructor(
         private courseService: CourseManagementService,
         private sessionStorageService: SessionStorageService,
         private alertService: AlertService,
     ) {}
 
-    async ngOnInit() {
+    ngOnInit() {
         this.loadAndFilterCourses();
     }
 
+    ngOnDestroy() {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }
 
     loadAndFilterCourses() {
         const clientId = this.sessionStorageService.retrieve('clientRegistrationId');
-        this.courseService.findAllOnlineCoursesWithRegistrationId(clientId).subscribe({
+        this.courseService.findAllOnlineCoursesWithRegistrationId(clientId)
+        .pipe(
+            takeUntil(this.destroy$),
+            catchError((error: HttpErrorResponse) => {
+                this.alertService.error('error.unexpectedError', {
+                    error: error.message,
+                });
+                return EMPTY;
+            })
+        ).subscribe({
             next: (courseResponse: OnlineCourseDtoModel[]) => {
                 this.courses = courseResponse;
-            },
-            error: (error) => {
-                this.alertService.error('error.unexpectedError', {
-                    error: error.message,
-                });
             },
         });
     }

This refactor:

  • Prevents memory leaks by properly managing subscriptions
  • Provides better error typing with HttpErrorResponse
  • Uses RxJS operators for error handling
  • Removes unnecessary async/await since we're using observables
src/main/webapp/app/lti/lti13-dynamic-registration.component.ts (3)

4-4: Use single quotes for string literals

According to the coding guidelines, string literals should use single quotes.

-import { TranslateDirective } from "../shared/language/translate.directive";
+import { TranslateDirective } from '../shared/language/translate.directive';

Line range hint 26-29: Subscribe to route params should be properly managed

The subscription to route params should be properly managed to prevent memory leaks.

+    private destroy$ = new Subject<void>();

     ngOnInit(): void {
-        this.route.params.subscribe((params) => {
+        this.route.params.pipe(
+            takeUntil(this.destroy$)
+        ).subscribe((params) => {
             this.courseId = Number(params['courseId']);
         });

Also add:

ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
}

Line range hint 44-54: Enhance error handling and user feedback

The error handling could be improved by:

  1. Providing specific error details for debugging
  2. Adding proper loading state management for UI feedback
  3. Adding type safety for window messaging
             .subscribe({
                 next: () => {
                     this.registeredSuccessfully = true;
                 },
-                error: () => {
+                error: (error: HttpErrorResponse) => {
                     this.registeredSuccessfully = false;
+                    console.error('LTI registration failed:', error);
                 },
             })
             .add(() => {
                 this.isRegistering = false;
-                (window.opener || window.parent).postMessage({ subject: 'org.imsglobal.lti.close' }, '*');
+                const targetWindow = (window.opener || window.parent) as Window;
+                targetWindow.postMessage({ subject: 'org.imsglobal.lti.close' }, '*');
             });
src/main/webapp/app/lti/lti13-select-content.component.ts (1)

1-6: Use single quotes for string literals

According to the coding guidelines, string literals should use single quotes.

Apply this diff to fix the quotes:

-import { Component, ElementRef, NgZone, OnInit, SecurityContext, ViewChild, inject } from "@angular/core";
-import { ActivatedRoute } from "@angular/router";
-import { DomSanitizer } from "@angular/platform-browser";
-import { TranslateDirective } from "../shared/language/translate.directive";
-import { FormsModule } from "@angular/forms";
-import { ArtemisSharedPipesModule } from "../shared/pipes/shared-pipes.module";
+import { Component, ElementRef, NgZone, OnInit, SecurityContext, ViewChild, inject } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { DomSanitizer } from '@angular/platform-browser';
+import { TranslateDirective } from '../shared/language/translate.directive';
+import { FormsModule } from '@angular/forms';
+import { ArtemisSharedPipesModule } from '../shared/pipes/shared-pipes.module';
src/main/webapp/app/lti/lti13-exercise-launch.component.ts (5)

9-9: Consider using absolute import path

Replace the relative import path with an absolute path for better maintainability.

-import { TranslateDirective } from '../shared/language/translate.directive';
+import { TranslateDirective } from 'app/shared/language/translate.directive';

Line range hint 41-67: Add CSRF protection and input validation

The sendRequest method handles sensitive authentication data but lacks some security measures:

  1. No CSRF protection in the POST request
  2. No validation of state and idToken parameters before use

Consider applying these security enhancements:

 sendRequest(): void {
     const state = this.route.snapshot.queryParamMap.get('state');
     const idToken = this.route.snapshot.queryParamMap.get('id_token');
 
-    if (!state || !idToken) {
+    if (!state?.match(/^[a-zA-Z0-9-_]+$/) || !idToken?.match(/^[a-zA-Z0-9-_]+\.([a-zA-Z0-9-_]+\.)?[a-zA-Z0-9-_]+$/)) {
         console.error('Required parameter for LTI launch missing');
         this.isLaunching = false;
         return;
     }
 
     const requestBody = new HttpParams().set('state', state).set('id_token', idToken);
 
     this.http
         .post<LtiLaunchResponse>('api/public/lti13/auth-login', requestBody.toString(), {
-            headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'),
+            headers: new HttpHeaders()
+                .set('Content-Type', 'application/x-www-form-urlencoded')
+                .set('X-XSRF-TOKEN', this.getCsrfToken()),
         })

Line range hint 127-146: Enhance security of session storage handling

The storeLtiSessionData method stores sensitive tokens in session storage without encryption:

  1. Consider using more secure storage methods
  2. Implement token expiration
  3. Add sanitization before storage

Consider implementing a secure storage service:

private secureStore(key: string, value: string): void {
    // Encrypt before storage
    const encryptedValue = this.encryptionService.encrypt(value);
    // Set expiration
    const data = {
        value: encryptedValue,
        expiry: Date.now() + (60 * 60 * 1000) // 1 hour
    };
    this.sessionStorageService.store(key, JSON.stringify(data));
}

Line range hint 148-156: Prevent potential memory leaks

The component subscribes to authentication state but doesn't unsubscribe, which could cause memory leaks.

Implement proper subscription management:

+private destroy$ = new Subject<void>();

 replaceWindowLocationWrapper(url: string): void {
     this.ltiService.setShownViaLti(true);
     this.themeService.applyThemePreference(Theme.LIGHT);
     const path = new URL(url).pathname;
     this.router.navigate([path], { replaceUrl: true });
 }

+ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
+}

Line range hint 51-67: Replace console.error with proper error logging

Error logging to console exposes sensitive information in production.

Use a proper logging service:

-console.error('Required parameter for LTI launch missing');
+this.loggingService.logError('LTI launch failed: missing parameters', { 
+    sensitive: false,
+    context: 'LTI Launch'
+});
src/main/webapp/app/lti/lti13-deep-linking.component.ts (4)

13-18: LGTM! Consider organizing imports.

The standalone component migration looks good. All necessary modules are imported and properly configured in the component decorator.

Consider organizing imports into these groups, separated by newlines:

  1. Angular core/common
  2. Application-specific imports
  3. Third-party libraries
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { HttpClient, HttpParams } from '@angular/common/http';
import { FormsModule } from '@angular/forms';

import { CourseManagementService } from 'app/course/manage/course-management.service';
import { Exercise } from 'app/entities/exercise.model';
import { SortService } from 'app/shared/service/sort.service';
import { AccountService } from 'app/core/auth/account.service';
import { Course } from 'app/entities/course.model';
import { AlertService } from 'app/core/util/alert.service';
import { ArtemisSharedModule } from 'app/shared/shared.module';
import { TranslateDirective } from '../shared/language/translate.directive';
import { ArtemisSharedComponentModule } from '../shared/components/shared-component.module';
import { ArtemisSharedCommonModule } from '../shared/shared-common.module';

import { faExclamationTriangle, faSort, faWrench } from '@fortawesome/free-solid-svg-icons';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { SessionStorageService } from 'ngx-webstorage';

Also applies to: 23-24


Line range hint 26-51: Prevent memory leaks by implementing proper subscription cleanup.

The component has multiple subscriptions that need to be cleaned up to prevent memory leaks.

Implement the following changes:

-export class Lti13DeepLinkingComponent implements OnInit {
+export class Lti13DeepLinkingComponent implements OnInit, OnDestroy {
+    private destroy$ = new Subject<void>();
+
     courseId: number;
     exercises: Exercise[];
     selectedExercises?: Set<number> = new Set();
     course: Course;

     // ... rest of the properties ...

     ngOnInit() {
-        this.route.params.subscribe((params) => {
+        this.route.params.pipe(
+            takeUntil(this.destroy$)
+        ).subscribe((params) => {
             // ... existing code ...
         });
     }

+    ngOnDestroy() {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }

Also update the redirectUserToLoginThenTargetLink method:

     redirectUserToLoginThenTargetLink(currentLink: string): void {
         this.router.navigate(['/']).then(() => {
-            this.accountService.getAuthenticationState().subscribe((user) => {
+            this.accountService.getAuthenticationState().pipe(
+                takeUntil(this.destroy$)
+            ).subscribe((user) => {
                 // ... existing code ...
             });
         });
     }

26-26: Add class-level documentation.

Missing JSDoc documentation for the component class.

Add comprehensive documentation:

+/**
+ * Component for handling LTI 1.3 deep linking functionality.
+ * Allows users to select exercises from a course and create deep links for LTI integration.
+ * Manages the selection, validation, and linking process while handling authentication
+ * and error states appropriately.
+ */
 export class Lti13DeepLinkingComponent implements OnInit {

Line range hint 147-167: Enhance error handling with specific error messages.

The error handling in sendDeepLinkRequest could be more specific to help with debugging.

Consider enhancing the error handling:

     sendDeepLinkRequest() {
         if (this.selectedExercises?.size) {
             // ... existing code ...

             this.http.post<DeepLinkingResponse>(`api/lti13/deep-linking/${this.courseId}`, null, { observe: 'response', params: httpParams }).subscribe({
                 next: (response) => {
                     if (response.status === 200) {
                         if (response.body) {
                             const targetLink = response.body.targetLinkUri;
                             window.location.replace(targetLink);
                         }
                     } else {
                         this.isLinking = false;
-                        this.alertService.error('artemisApp.lti13.deepLinking.unknownError');
+                        this.alertService.error('artemisApp.lti13.deepLinking.responseError', { status: response.status });
                     }
                 },
                 error: (error) => {
                     this.isLinking = false;
-                    onError(this.alertService, error);
+                    const errorKey = error.status === 403 ? 'artemisApp.lti13.deepLinking.unauthorized' :
+                        error.status === 404 ? 'artemisApp.lti13.deepLinking.notFound' :
+                        'artemisApp.lti13.deepLinking.unknownError';
+                    this.alertService.error(errorKey, { status: error.status });
                 },
             });
         } else {
             this.alertService.error('artemisApp.lti13.deepLinking.selectToLink');
         }
     }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between b365494 and 56dae8c.

📒 Files selected for processing (7)
  • src/main/webapp/app/lti/lti-course-card.component.ts (1 hunks)
  • src/main/webapp/app/lti/lti.module.ts (1 hunks)
  • src/main/webapp/app/lti/lti13-deep-linking.component.ts (1 hunks)
  • src/main/webapp/app/lti/lti13-dynamic-registration.component.ts (1 hunks)
  • src/main/webapp/app/lti/lti13-exercise-launch.component.ts (2 hunks)
  • src/main/webapp/app/lti/lti13-select-content.component.ts (1 hunks)
  • src/main/webapp/app/lti/lti13-select-course.component.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/webapp/app/lti/lti.module.ts
🧰 Additional context used
📓 Path-based instructions (6)
src/main/webapp/app/lti/lti-course-card.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/lti/lti13-deep-linking.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/lti/lti13-dynamic-registration.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/lti/lti13-exercise-launch.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/lti/lti13-select-content.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/lti/lti13-select-course.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

🔇 Additional comments (9)
src/main/webapp/app/lti/lti-course-card.component.ts (1)

5-7: LGTM! Imports follow Angular guidelines.

The new imports are correctly organized and follow the Angular style guide recommendations.

src/main/webapp/app/lti/lti13-select-course.component.ts (3)

6-7: LGTM! Import statements are properly structured.

The new imports for standalone components follow Angular style guidelines and match the component's declared dependencies.


12-13: LGTM! Component decorator properly configured for standalone mode.

The standalone configuration and imports are correctly implemented.


Line range hint 1-39: Verify string literal consistency across the component.

Let's ensure all string literals follow the single quotes convention as per coding guidelines.

✅ Verification successful

String literals in the component follow the single quotes convention

The TypeScript component correctly uses single quotes for all string literals:

  • Import paths: '@angular/core', 'ngx-webstorage', etc.
  • Component selector: 'jhi-lti-courses-overview'
  • Template URL: './lti13-select-course.component.html'
  • Storage key: 'clientRegistrationId'
  • Error message: 'error.unexpectedError'

The double quotes in the HTML template file are appropriate as they follow HTML conventions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for double-quoted strings in the component and template
rg -l "\"[^\"]+\"" "src/main/webapp/app/lti/lti13-select-course.component.*"

Length of output: 171


Script:

#!/bin/bash
# First, let's find the exact file paths
fd "lti13-select-course.component" src/main/webapp

# Then check for double-quoted strings in those files
rg -l "\"[^\"]+\"" $(fd "lti13-select-course.component" src/main/webapp)

Length of output: 411


Script:

#!/bin/bash
# Let's see the actual content to verify string usage in both files
echo "=== TypeScript Component ==="
rg -A 1 "'" src/main/webapp/app/lti/lti13-select-course.component.ts
echo -e "\n=== Double quotes in TypeScript Component ==="
rg -A 1 "\"" src/main/webapp/app/lti/lti13-select-course.component.ts
echo -e "\n=== Template File ==="
cat src/main/webapp/app/lti/lti13-select-course.component.html

Length of output: 1946

src/main/webapp/app/lti/lti13-dynamic-registration.component.ts (2)

9-10: LGTM! Good standalone component setup

The component is correctly configured as standalone with the necessary TranslateDirective import, which aligns with modern Angular practices and the localization requirements.


Line range hint 12-54: Add unit tests for component logic

The PR objectives emphasize the importance of integration tests. Please ensure proper test coverage for:

  1. Successful registration flow
  2. Error handling scenarios
  3. Window message posting
  4. Route parameter handling
src/main/webapp/app/lti/lti13-select-content.component.ts (3)

18-19: LGTM! Good job on making the component standalone

The component is correctly configured as standalone with the necessary imports. This change improves modularity and follows modern Angular practices.


22-24: LGTM! Good use of modern DI pattern

The dependency injection using the inject() function is well implemented. The approach:

  • Reduces boilerplate compared to constructor injection
  • Maintains proper typing
  • Follows the no-prefix guideline for private members

Line range hint 56-58: Verify complete XSS protection for deep linking

While the code correctly uses sanitize() for the URL, let's verify that all potential XSS vectors are covered.

✅ Verification successful

Based on the results, I can generate the final response as we have sufficient information about the security measures in place:

Deep linking implementation has proper XSS protection

The code implements comprehensive XSS protection for deep linking through:

  • Proper URL sanitization using both DomSanitizer and SecurityContext.URL
  • No direct DOM manipulation through innerHTML/outerHTML that could bypass sanitization
  • Strict parameter validation before form submission
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other deep linking related security measures
rg -l "deepLink" | xargs rg "sanitize|SecurityContext|DomSanitizer" -A 3

# Check for any direct DOM manipulation that might bypass sanitization
ast-grep --pattern 'innerHTML = $_' 
ast-grep --pattern 'outerHTML = $_'

Length of output: 3569

@github-actions github-actions bot added the tests label Dec 3, 2024
@raffifasaro raffifasaro marked this pull request as ready for review December 4, 2024 13:57
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: 3

🧹 Outside diff range and nitpick comments (19)
src/main/webapp/app/lti/lti-course-card.component.ts (1)

18-18: Consider using computed() for courseColor

The current implementation uses effect() to update courseColor, but since it's a derived state from course input, consider using computed() instead:

-courseColor: string;
+courseColor = computed(() => {
+    const courseValue = this.course();
+    return courseValue?.color || this.ARTEMIS_DEFAULT_COLOR;
+});

-constructor() {
-    effect(() => {
-        const courseValue = this.course();
-        this.courseColor = courseValue?.color || this.ARTEMIS_DEFAULT_COLOR;
-    });
-}

Also applies to: 22-26

src/main/webapp/app/overview/exercise-details/lti-initializer-modal.component.ts (1)

1-1: Consider using signals for form state

Since we're adopting Angular 18 practices, consider using signals for the form state:

-import { Component, inject } from '@angular/core';
+import { Component, inject, signal } from '@angular/core';

export class LtiInitializerModalComponent {
    private activeModal = inject(NgbActiveModal);
    private alertService = inject(AlertService);
    private router = inject(Router);
    private activatedRoute = inject(ActivatedRoute);

-   password: string;
-   loginName: string;
-   readAndUnderstood = false;
+   password = signal('');
+   loginName = signal('');
+   readAndUnderstood = signal(false);

Also applies to: 11-14

src/main/webapp/app/lti/lti-course-card.component.html (2)

5-5: Avoid duplicate routerLink

The routerLink is duplicated in both the div and anchor elements. Consider consolidating:

<div
    id="course-{{ course()?.id }}-header"
    class="card-header text-white"
-   [routerLink]="['/lti/exercises', course()?.id]"
    [ngStyle]="{ '--background-color-for-hover': courseColor }"
>
    <a class="stretched-link" [routerLink]="['/lti/exercises', course()?.id]"></a>

Also applies to: 8-8


18-18: Consider using computed signal for title

To improve performance by reducing template expressions:

// In component.ts
+fullTitle = computed(() => {
+    const c = this.course();
+    return c ? `${c.title} (${c.shortName})` : '';
+});

// In template
-{{ course()?.title + ' (' + course()?.shortName + ')' }}
+{{ fullTitle() }}
src/main/webapp/app/lti/lti13-select-course.component.ts (2)

Line range hint 23-25: Add error handling for async initialization

The async ngOnInit should include try-catch error handling to prevent unhandled promise rejections.

-    async ngOnInit() {
-        this.loadAndFilterCourses();
+    async ngOnInit() {
+        try {
+            await this.loadAndFilterCourses();
+        } catch (error) {
+            this.alertService.error('error.unexpectedError', { error: error.message });
+        }

21-21: Initialize courses array with proper typing

The courses property should be initialized to prevent potential undefined errors.

-    public courses: OnlineCourseDtoModel[];
+    public courses: OnlineCourseDtoModel[] = [];
src/main/webapp/app/overview/exercise-details/lti-initializer.component.ts (1)

Line range hint 24-43: Address potential memory leaks and security concerns

Several issues need attention:

  1. Subscription cleanup missing
  2. Password exposed in component instance
  3. Nested subscriptions can lead to memory leaks
+    private destroy$ = new Subject<void>();

     ngOnInit() {
-        this.activatedRoute.queryParams.subscribe((queryParams) => {
+        this.activatedRoute.queryParams.pipe(
+            takeUntil(this.destroy$),
+            filter(params => params['initialize'] !== undefined)
+        ).subscribe((queryParams) => {
-            if (queryParams['initialize'] !== undefined) {
                 this.userService.initializeLTIUser().subscribe((res) => {
                     const password = res.body?.password;
                     if (!password) {
                         this.alertService.info('artemisApp.lti.initializationError');
                         this.router.navigate([], {
                             relativeTo: this.activatedRoute,
                             queryParams: { initialize: null },
                             queryParamsHandling: 'merge',
                         });
                         return;
                     }
                     this.modalRef = this.modalService.open(LtiInitializerModalComponent, { size: 'lg', backdrop: 'static', keyboard: false });
                     this.modalRef.componentInstance.loginName = this.accountService.userIdentity?.login;
-                    this.modalRef.componentInstance.password = password;
+                    // Set password and immediately clear it from memory
+                    this.modalRef.componentInstance.password = password;
+                    setTimeout(() => password = undefined, 0);
                 });
-            }
         });
     }

+    ngOnDestroy() {
+        this.destroy$.next();
+        this.destroy$.complete();
+        if (this.modalRef) {
+            this.modalRef.close();
+        }
+    }
src/main/webapp/app/admin/lti-configuration/lti-configuration.service.ts (2)

Line range hint 25-27: Improve type safety and error handling

The HTTP methods should use more specific types than 'any' for better type safety.

-    addLtiPlatformConfiguration(ltiPlatformConfiguration: LtiPlatformConfiguration): Observable<HttpResponse<any>> {
+    addLtiPlatformConfiguration(ltiPlatformConfiguration: LtiPlatformConfiguration): Observable<HttpResponse<LtiPlatformConfiguration>> {

-    updateLtiPlatformConfiguration(ltiPlatformConfiguration: LtiPlatformConfiguration): Observable<HttpResponse<any>> {
+    updateLtiPlatformConfiguration(ltiPlatformConfiguration: LtiPlatformConfiguration): Observable<HttpResponse<LtiPlatformConfiguration>> {

Also applies to: 34-36


Line range hint 15-20: Consider using environment configuration for API endpoints

API endpoints should be configurable through environment files for better maintainability.

+    private readonly API_BASE = environment.apiUrl;

     query(req?: any): Observable<HttpResponse<LtiPlatformConfiguration[]>> {
         const params: HttpParams = createRequestOption(req);
-        return this.http.get<LtiPlatformConfiguration[]>('api/lti-platforms', {
+        return this.http.get<LtiPlatformConfiguration[]>(`${this.API_BASE}/lti-platforms`, {

Also applies to: 25-27, 34-36, 42-44, 46-48

src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.ts (1)

15-17: Add type annotations to inject calls

For better type safety and code clarity, explicitly specify the return type of inject calls.

-    private route = inject(ActivatedRoute);
-    private sortService = inject(SortService);
-    private courseManagementService = inject(CourseManagementService);
+    private route: ActivatedRoute = inject(ActivatedRoute);
+    private sortService: SortService = inject(SortService);
+    private courseManagementService: CourseManagementService = inject(CourseManagementService);
src/main/webapp/app/admin/lti-configuration/edit-lti-configuration.component.ts (2)

Line range hint 33-43: Fix duplicate form initialization in ngOnInit

The initializeForm() is called twice: once unconditionally at the end of ngOnInit and once within the subscription callback. This could lead to unnecessary form resets.

 ngOnInit() {
     const platformId = this.route.snapshot.paramMap.get('platformId');
     if (platformId) {
         this.ltiConfigurationService.getLtiPlatformById(Number(platformId)).subscribe({
             next: (data) => {
                 this.platform = data;
                 this.initializeForm();
             },
             error: (error) => {
                 this.alertService.error(error);
             },
         });
+    } else {
+        this.initializeForm();
     }
-    this.initializeForm();
 }

15-18: Add type annotations to inject calls

For better type safety and code clarity, explicitly specify the return type of inject calls.

-    private route = inject(ActivatedRoute);
-    private ltiConfigurationService = inject(LtiConfigurationService);
-    private router = inject(Router);
-    private alertService = inject(AlertService);
+    private route: ActivatedRoute = inject(ActivatedRoute);
+    private ltiConfigurationService: LtiConfigurationService = inject(LtiConfigurationService);
+    private router: Router = inject(Router);
+    private alertService: AlertService = inject(AlertService);
src/main/webapp/app/course/manage/course-lti-configuration/edit-course-lti-configuration.component.ts (3)

22-25: Add type annotations to inject calls

For better type safety and code clarity, explicitly specify the return type of inject calls.

-    private route = inject(ActivatedRoute);
-    private courseService = inject(CourseManagementService);
-    private router = inject(Router);
-    private ltiConfigurationService = inject(LtiConfigurationService);
+    private route: ActivatedRoute = inject(ActivatedRoute);
+    private courseService: CourseManagementService = inject(CourseManagementService);
+    private router: Router = inject(Router);
+    private ltiConfigurationService: LtiConfigurationService = inject(LtiConfigurationService);

Line range hint 71-79: Add type safety to loadData parameters

The query parameters object would benefit from strong typing to prevent potential runtime errors.

+    interface QueryParams {
+        page: number;
+        size: number;
+        sort: string[];
+    }
+
     loadData(): void {
         this.ltiConfigurationService
-            .query({
+            .query(<QueryParams>{
                 page: this.page - 1,
                 size: this.itemsPerPage,
                 sort: ['id', 'asc'],
             })
             .subscribe((res: HttpResponse<LtiPlatformConfiguration[]>) => this.onSuccess(res.body, res.headers));
     }

Line range hint 17-21: Convert to standalone component as per PR objectives

The PR objectives mention migrating to standalone components, but this component is still using the traditional module-based approach.

 @Component({
+    standalone: true,
+    imports: [
+        CommonModule,
+        RouterModule,
+        ReactiveFormsModule
+    ],
     selector: 'jhi-edit-course-lti-configuration',
     templateUrl: './edit-course-lti-configuration.component.html',
 })
src/main/webapp/app/admin/lti-configuration/lti-configuration.component.ts (1)

Line range hint 144-154: Consider streamlining error handling.

The method currently uses both dialogErrorSource and alertService for error handling. Consider consolidating to use just the alertService for a more consistent approach across the application.

    deleteLtiPlatform(platformId: number): void {
        this.ltiConfigurationService.deleteLtiPlatform(platformId).subscribe({
            next: () => {
-               this.dialogErrorSource.next('');
                this.router.navigate(['admin', 'lti-configuration']);
            },
            error: (error: HttpErrorResponse) => {
-               this.dialogErrorSource.next(error.message);
                this.alertService.error('artemisApp.lti13.deletePlatformError');
            },
        });
    }
src/main/webapp/app/lti/lti13-exercise-launch.component.ts (1)

Line range hint 46-71: Enhance security measures for token handling.

Consider implementing additional security measures:

  1. Sanitize and validate state and idToken parameters before use
  2. Avoid exposing login information in error responses
  3. Add rate limiting for the auth-login endpoint
    sendRequest(): void {
        const state = this.route.snapshot.queryParamMap.get('state');
        const idToken = this.route.snapshot.queryParamMap.get('id_token');

+       // Validate token format and length
+       if (!this.isValidToken(state) || !this.isValidToken(idToken)) {
+           console.error('Invalid token format');
+           this.isLaunching = false;
+           return;
+       }

        if (!state || !idToken) {
            console.error('Required parameter for LTI launch missing');
            this.isLaunching = false;
            return;
        }
src/main/webapp/app/lti/lti13-deep-linking.component.ts (2)

23-24: Consider optimizing module imports.

The component imports several shared modules which might include unused features. Consider breaking down the imports to include only the necessary components and directives for better tree-shaking.


Line range hint 156-185: Enhance security and error handling in deep linking.

  1. Add token validation before making the request
  2. Use more specific error messages based on error types
  3. Consider implementing retry logic for network failures
    sendDeepLinkRequest() {
        if (this.selectedExercises?.size) {
            const ltiIdToken = this.sessionStorageService.retrieve('ltiIdToken') ?? '';
            const clientRegistrationId = this.sessionStorageService.retrieve('clientRegistrationId') ?? '';
+           
+           if (!this.isValidToken(ltiIdToken) || !this.isValidToken(clientRegistrationId)) {
+               this.alertService.error('artemisApp.lti13.deepLinking.invalidToken');
+               return;
+           }

            const exerciseIds = Array.from(this.selectedExercises).join(',');
            // ... rest of the method
        }
    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 56dae8c and a6dc93a.

📒 Files selected for processing (13)
  • src/main/webapp/app/admin/lti-configuration/edit-lti-configuration.component.ts (2 hunks)
  • src/main/webapp/app/admin/lti-configuration/lti-configuration.component.ts (2 hunks)
  • src/main/webapp/app/admin/lti-configuration/lti-configuration.service.ts (1 hunks)
  • src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.ts (2 hunks)
  • src/main/webapp/app/course/manage/course-lti-configuration/edit-course-lti-configuration.component.ts (2 hunks)
  • src/main/webapp/app/lti/lti-course-card.component.html (1 hunks)
  • src/main/webapp/app/lti/lti-course-card.component.ts (1 hunks)
  • src/main/webapp/app/lti/lti13-deep-linking.component.ts (2 hunks)
  • src/main/webapp/app/lti/lti13-dynamic-registration.component.ts (1 hunks)
  • src/main/webapp/app/lti/lti13-exercise-launch.component.ts (2 hunks)
  • src/main/webapp/app/lti/lti13-select-course.component.ts (1 hunks)
  • src/main/webapp/app/overview/exercise-details/lti-initializer-modal.component.ts (2 hunks)
  • src/main/webapp/app/overview/exercise-details/lti-initializer.component.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/webapp/app/lti/lti13-dynamic-registration.component.ts
🧰 Additional context used
📓 Path-based instructions (12)
src/main/webapp/app/admin/lti-configuration/edit-lti-configuration.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/admin/lti-configuration/lti-configuration.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/lti/lti-course-card.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/admin/lti-configuration/lti-configuration.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/course/manage/course-lti-configuration/course-lti-configuration.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/overview/exercise-details/lti-initializer.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/lti/lti-course-card.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/lti/lti13-select-course.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/course/manage/course-lti-configuration/edit-course-lti-configuration.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/overview/exercise-details/lti-initializer-modal.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/lti/lti13-exercise-launch.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/lti/lti13-deep-linking.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

🔇 Additional comments (8)
src/main/webapp/app/lti/lti-course-card.component.ts (2)

1-1: LGTM: Imports are correctly organized

The imports are properly organized with core Angular imports first, followed by application-specific imports. The standalone component dependencies (RouterLink, NgStyle, ArtemisSharedModule) are correctly imported.

Also applies to: 5-7


13-14: Verify test files update for standalone migration

While the standalone configuration is correct, the test files still need to be updated to match the new standalone setup.

#!/bin/bash
# Description: Check if test files have been updated for standalone components
rg -t ts "TestBed.configureTestingModule.*LtiCourseCardComponent" "src/test"
src/main/webapp/app/lti/lti13-select-course.component.ts (2)

12-13: LGTM: Proper standalone component configuration

The component is correctly configured as standalone with the necessary imports.


16-18: LGTM: Modern dependency injection pattern

Correctly uses the new inject() pattern for service dependencies.

src/main/webapp/app/overview/exercise-details/lti-initializer.component.ts (1)

14-19: LGTM: Clean dependency injection implementation

Services are properly injected using the modern inject() pattern.

src/main/webapp/app/admin/lti-configuration/lti-configuration.service.ts (1)

9-9: LGTM: Proper dependency injection

The HttpClient is correctly injected using the modern inject() pattern.

src/main/webapp/app/admin/lti-configuration/lti-configuration.component.ts (1)

20-24: LGTM! Clean dependency injection implementation.

The migration to using the inject() function follows Angular 18's best practices for dependency injection.

src/main/webapp/app/lti/lti13-exercise-launch.component.ts (1)

21-22: LGTM! CommonModule properly imported.

The addition of CommonModule addresses the previous review comment about the [hidden] directive dependency.

Comment on lines +11 to +14
private activeModal = inject(NgbActiveModal);
private alertService = inject(AlertService);
private router = inject(Router);
private activatedRoute = inject(ActivatedRoute);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider converting to standalone component

While the migration to inject() is good, consider completing the Angular 18 migration by converting this to a standalone component:

@Component({
    selector: 'jhi-lti-initializer-modal',
    templateUrl: './lti-initializer-modal.component.html',
+   standalone: true,
+   imports: [NgbActiveModal, FormsModule]
})

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +12 to +14
<ng-container *ngIf="course()?.courseIcon">
<jhi-secured-image [cachingStrategy]="CachingStrategy.LOCAL_STORAGE" [src]="course()?.courseIcon || ''"></jhi-secured-image>
</ng-container>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

*Use @if instead of ngIf for better performance

As per the coding guidelines, @if should be used instead of *ngIf:

-<ng-container *ngIf="course()?.courseIcon">
+@if (course()?.courseIcon) {
    <jhi-secured-image [cachingStrategy]="CachingStrategy.LOCAL_STORAGE" [src]="course()?.courseIcon || ''"></jhi-secured-image>
-</ng-container>
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ng-container *ngIf="course()?.courseIcon">
<jhi-secured-image [cachingStrategy]="CachingStrategy.LOCAL_STORAGE" [src]="course()?.courseIcon || ''"></jhi-secured-image>
</ng-container>
@if (course()?.courseIcon) {
<jhi-secured-image [cachingStrategy]="CachingStrategy.LOCAL_STORAGE" [src]="course()?.courseIcon || ''"></jhi-secured-image>
}

@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Convert to standalone component as per PR objectives

The PR objectives mention migrating to standalone components, but this component is still using the traditional module-based approach. Consider converting it to a standalone component.

-@Component({
+@Component({
+    standalone: true,
+    imports: [CommonModule, RouterModule],
     selector: 'jhi-course-lti-configuration',
     templateUrl: './course-lti-configuration.component.html',
 })

Also applies to: 10-14

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!) ready for review tests
Projects
Status: Ready For Review
Development

Successfully merging this pull request may close these issues.

1 participant