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: Restrict Course Overview Access #9834

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

Conversation

cremertim
Copy link
Contributor

@cremertim cremertim commented Nov 20, 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 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.

Motivation and Context

If the users want to, they can always access any of the subpages in the Course Overview, regardless of the course configuration. This is possible by inserting the corresponding url e.g /courses/{courseId}/faq

Description

I implemented an CourseOverviewGuardService to restrict access of the sites

Steps for Testing

Prerequisites:

  • 1 Instructor
  • 2 Students
  • 1 Programming Exercise with Complaints enabled
  1. Log in to Artemis.
  2. Navigate to the Course Overview page.
  3. Verify the following for the Exams tab:
    • You can access the Exams tab if a visible exam exists (create one if necessary).
    • You cannot access the Exams tab if no visible exam exists.
  4. Verify the following for the Competencies tab:
    • You can access the Competencies tab if competencies or prerequisites are present (create some if needed).
    • You cannot access the Competencies tab if none competencies and prerequisites are present.
  5. Verify the following for the Tutorial Groups tab:
    • You can access the Tutorial Groups tab if at least one tutorial group exists (create one if necessary).
    • You cannot access the Tutorial Groups tab if none exist.
  6. Verify the following for the FAQs tab:
    • You can access the FAQs tab if it is enabled in the course settings.
    • You cannot access the FAQs tab if it is disabled in the course settings.
  7. Verify the following for the Learning Paths tab:
    • You can access the Learning Paths tab if they are enabled in the course settings.
    • You cannot access the Learning Paths tab if they are disabled in the course settings.
  8. Verify the following:
    • You are redirected to the Exercises tab if you cannot access a specific feature.

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

Class/File Line Coverage Confirmation (assert/expect)
course-overview-guard.ts 98%

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced CourseOverviewGuard to manage access to course overview routes.
    • Added methods to handle route activation and visibility checks for associated exams.
  • Bug Fixes

    • Removed the outdated CourseDashboardGuard for improved access control across various routes.
  • Chores

    • Updated routing configuration to integrate the new guard, enhancing overall route management and consistency in path definitions.
    • Centralized path definitions using an enum for better organization.

@cremertim cremertim requested a review from a team as a code owner November 20, 2024 14:46
@github-actions github-actions bot added the client Pull requests that update TypeScript code. (Added Automatically!) label Nov 20, 2024
@cremertim cremertim changed the title Development Restrict Course Overview Access in the Frontend Development: Restrict Course Overview Access in the Frontend Nov 20, 2024
@cremertim cremertim changed the title Development: Restrict Course Overview Access in the Frontend Development: Restrict Course Overview Access Nov 20, 2024
Copy link

coderabbitai bot commented Nov 20, 2024

Walkthrough

The changes in this pull request involve the removal of the CourseDashboardGuard service and its replacement with a new service called CourseOverviewGuard. The routing configuration has been updated to use an enum for path definitions, enhancing organization. The new guard implements access control for various course overview routes, ensuring that access is determined based on course data and user permissions. Additionally, unit tests for the new guard have been added to validate its functionality.

Changes

File Path Change Summary
src/main/webapp/app/overview/course-dashboard-guard.service.ts Deleted CourseDashboardGuard class.
src/main/webapp/app/overview/courses-routing.module.ts Introduced CourseOverviewRoutePath enum; updated routes to use this enum; replaced CourseDashboardGuard with CourseOverviewGuard.
src/main/webapp/app/overview/course-overview-guard.ts Added CourseOverviewGuard class with methods canActivate, handleReturn, and hasVisibleExams.
src/test/javascript/spec/component/overview/course-overview-guard.spec.ts Added unit tests for CourseOverviewGuard, covering canActivate and handleReturn methods.

Possibly related PRs

Suggested labels

ready for review, server, core, user interface

Suggested reviewers

  • SimonEntholzer
  • JohannesStoehr
  • krusche
  • florian-glombik
  • pzdr7

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 889be36 and 449375b.

📒 Files selected for processing (1)
  • src/test/javascript/spec/component/overview/course-overview-guard.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/javascript/spec/component/overview/course-overview-guard.spec.ts

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 (10)
src/main/webapp/app/overview/courses-routing.module.ts (4)

200-200: Consider adding UserRouteAccessService for consistency.

Other protected routes use both UserRouteAccessService and CourseOverviewGuardService. For consistency, consider adding UserRouteAccessService here as well.

-canActivate: [CourseOverviewGuardService],
+canActivate: [UserRouteAccessService, CourseOverviewGuardService],

Line range hint 221-235: Add CourseOverviewGuardService to the child route.

The tutorial group details route should also be protected by CourseOverviewGuardService to prevent direct access.

 {
     path: ':tutorialGroupId',
     component: CourseTutorialGroupDetailComponent,
     data: {
         authorities: [Authority.USER],
         pageTitle: 'overview.tutorialGroups',
         hasSidebar: true,
         showRefreshButton: true,
     },
-    canActivate: [UserRouteAccessService],
+    canActivate: [UserRouteAccessService, CourseOverviewGuardService],
     loadChildren: () => import('../overview/tutorial-group-details/course-tutorial-group-details.module').then((m) => m.CourseTutorialGroupDetailsModule),
 },

280-280: Consider adding UserRouteAccessService for consistency.

For consistency with other routes and to ensure proper authentication, consider adding UserRouteAccessService.

-canActivate: [CourseOverviewGuardService],
+canActivate: [UserRouteAccessService, CourseOverviewGuardService],

Line range hint 170-280: Review the overall route protection strategy.

The current implementation shows inconsistencies in how routes are protected:

  1. Some routes use both guards while others only use CourseOverviewGuardService
  2. Child routes generally lack the CourseOverviewGuardService protection
  3. There's no clear pattern for when both guards should be used vs. just one

Consider:

  1. Documenting the guard usage strategy
  2. Implementing a consistent approach across all routes
  3. Creating a combined guard that incorporates both user access and course-specific checks
src/main/webapp/app/overview/course-overview-guard.service.ts (6)

20-22: Correct typos in method documentation.

There are a couple of typos in the method documentation that should be corrected for clarity:

  • "route route" should be "route".
  • "CourseD" should be "course".

Apply this diff to fix the typos:

 /**
- * Check if the client can activate a course overview route route.
- * @return true if CourseD is enabled for this instance, false otherwise
+ * Check if the client can activate a course overview route.
+ * @return true if the course is enabled for this instance, false otherwise
  */

14-17: Prefer constructor injection over inject() for dependency injection.

While the inject() function is valid, using constructor injection is recommended for services according to the Angular Style Guide. Constructor injection improves readability and testability.

Refactor the service to use constructor injection:

 export class CourseOverviewGuardService implements CanActivate {
-    private courseStorageService = inject(CourseStorageService);
-    private courseManagementService = inject(CourseManagementService);
-    private router = inject(Router);
-    private serverDateService = inject(ArtemisServerDateService);
+    constructor(
+        private courseStorageService: CourseStorageService,
+        private courseManagementService: CourseManagementService,
+        private router: Router,
+        private serverDateService: ArtemisServerDateService,
+    ) {}

Ensure to update any references to this.courseStorageService, this.courseManagementService, this.router, and this.serverDateService accordingly.


49-77: Refactor handleReturn to be a class method.

Declaring handleReturn as a class method instead of an arrow function enhances code consistency and aligns with the Angular Style Guide.

Apply this diff to refactor the method:

-export class CourseOverviewGuardService implements CanActivate {
-    // ...
-    handleReturn = (course?: Course, type?: string): Observable<boolean> => {
+    handleReturn(course?: Course, type?: string): Observable<boolean> {
         let hasAccess: boolean;
         // Rest of the method remains the same

Remember to remove the equals sign and the arrow function syntax.


79-88: Optimize hasVisibleExams method with some function.

You can simplify the exam visibility check using the some array method for better readability.

Refactor the method as follows:

 hasVisibleExams(course?: Course): boolean {
-    if (course?.exams) {
-        for (const exam of course.exams) {
-            if (exam.visibleDate && dayjs(exam.visibleDate).isBefore(this.serverDateService.now())) {
-                return true;
-            }
-        }
-    }
-    return false;
+    return course?.exams?.some(
+        (exam) => exam.visibleDate && dayjs(exam.visibleDate).isBefore(this.serverDateService.now()),
+    ) ?? false;
 }

23-47: Handle potential errors when fetching course data.

Currently, there's no error handling in the observable pipeline when fetching the course. Unhandled errors could lead to unexpected behavior.

Consider adding error handling to the observable chain:

 return this.courseManagementService.find(courseIdNumber).pipe(
     switchMap((res) => {
         if (res.body) {
             // Store course in cache
             this.courseStorageService.updateCourse(res.body);
         }
         // Flatten the result to return Observable<boolean> directly
         return this.handleReturn(this.courseStorageService.getCourse(courseIdNumber), path);
+    }),
+    catchError(() => {
+        // Handle the error, possibly redirect or return false
+        return of(false);
     }),
 );

Ensure to import catchError from rxjs/operators.


Line range hint 93-93: Add unit tests for CourseOverviewGuardService.

Including meaningful unit tests will help ensure the guard functions correctly under various scenarios.

Would you like assistance in creating unit tests for this service?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e3ed347 and 36ba843.

📒 Files selected for processing (3)
  • src/main/webapp/app/overview/course-dashboard/course-dashboard-guard.service.ts (0 hunks)
  • src/main/webapp/app/overview/course-overview-guard.service.ts (1 hunks)
  • src/main/webapp/app/overview/courses-routing.module.ts (7 hunks)
💤 Files with no reviewable changes (1)
  • src/main/webapp/app/overview/course-dashboard/course-dashboard-guard.service.ts
🧰 Additional context used
📓 Path-based instructions (2)
src/main/webapp/app/overview/course-overview-guard.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/overview/courses-routing.module.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 (4)
src/main/webapp/app/overview/courses-routing.module.ts (4)

15-15: LGTM: Import changes align with the new guard implementation.

The import of CourseOverviewGuardService is correctly added to support the new access control mechanism.


189-189: LGTM: Comprehensive route protection with both guards.

The combination of UserRouteAccessService and CourseOverviewGuardService provides both authentication and course-specific access control.


Line range hint 170-182: Consider adding the guard to child routes.

While the parent route is protected by CourseOverviewGuardService, the child routes (especially the competency details route) should also be protected to prevent direct access.

Consider adding the guard to the child routes:

 {
     path: ':competencyId',
+    canActivate: [CourseOverviewGuardService],
     loadChildren: () => import('../overview/course-competencies/course-competencies-details.module').then((m) => m.ArtemisCourseCompetenciesDetailsModule),
 },

Line range hint 246-261: Verify exam participation access requirements.

  1. The exam participation route should be protected by CourseOverviewGuardService to prevent direct access.
  2. Please verify if there are any special access requirements for exam participation that need to be considered.
 {
     path: ':examId',
     component: ExamParticipationComponent,
     data: {
         authorities: [Authority.USER],
         pageTitle: 'overview.exams',
         hasSidebar: true,
         showRefreshButton: true,
     },
-    canActivate: [UserRouteAccessService],
+    canActivate: [UserRouteAccessService, CourseOverviewGuardService],
     canDeactivate: [PendingChangesGuard],
     loadChildren: () => import('../exam/participate/exam-participation.module').then((m) => m.ArtemisExamParticipationModule),
 },

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 (3)
src/main/webapp/app/overview/course-overview-guard.service.ts (3)

51-72: Simplify route access logic using an object literal pattern.

The switch statement could be simplified for better maintainability and readability.

-        switch (type) {
-            case 'exams':
-                hasAccess = this.hasVisibleExams(course);
-                break;
-            case 'competencies':
-                hasAccess = !!(course?.numberOfCompetencies || course?.numberOfPrerequisites);
-                break;
-            // ... other cases
-            default:
-                hasAccess = false;
-        }
+        const accessChecks = {
+            'exams': () => this.hasVisibleExams(course),
+            'competencies': () => !!(course?.numberOfCompetencies || course?.numberOfPrerequisites),
+            'tutorial-groups': () => !!course?.numberOfTutorialGroups,
+            'dashboard': () => course?.studentCourseAnalyticsDashboardEnabled ?? false,
+            'faq': () => course?.faqEnabled ?? false,
+            'learning-path': () => course?.learningPathsEnabled ?? false
+        };
+        hasAccess = type ? (accessChecks[type]?.() ?? false) : false;

79-88: Optimize exam visibility check using Array.some().

The current implementation can be simplified and made more efficient by using Array methods.

     hasVisibleExams(course?: Course): boolean {
-        if (course?.exams) {
-            for (const exam of course.exams) {
-                if (exam.visibleDate && dayjs(exam.visibleDate).isBefore(this.serverDateService.now())) {
-                    return true;
-                }
-            }
-        }
-        return false;
+        return course?.exams?.some(
+            exam => exam.visibleDate && dayjs(exam.visibleDate).isBefore(this.serverDateService.now())
+        ) ?? false;
     }

Also, consider using UTC dates for comparison to avoid timezone-related issues:

-                    dayjs(exam.visibleDate).isBefore(this.serverDateService.now())
+                    dayjs.utc(exam.visibleDate).isBefore(this.serverDateService.now())

1-89: Consider implementing a caching strategy for course data.

To improve performance and reduce server load:

  1. Implement a TTL (Time-To-Live) for cached course data
  2. Consider prefetching course data for likely navigation paths
  3. Add error boundaries for failed course fetches

This would enhance the user experience by reducing loading times and providing better error handling.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 36ba843 and 2d4aba9.

📒 Files selected for processing (1)
  • src/main/webapp/app/overview/course-overview-guard.service.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/main/webapp/app/overview/course-overview-guard.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

🔇 Additional comments (1)
src/main/webapp/app/overview/course-overview-guard.service.ts (1)

1-18: LGTM! Well-structured service setup.

The service follows Angular best practices with proper dependency injection using the modern inject() function and appropriate class naming conventions.

SimonKaran13
SimonKaran13 previously approved these changes Nov 21, 2024
Copy link

@SimonKaran13 SimonKaran13 left a comment

Choose a reason for hiding this comment

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

I tested the following on TS6

  • I tested the Exams tab is accessible when a visible exam exists and inaccessible otherwise.
  • Similarly, the Competencies tab is accessible only when competencies or prerequisites are present.
  • The Tutorial Groups tab requires at least one tutorial group to be visible.
  • For the FAQs tab, access depends on whether it is enabled in the course settings.
  • Learning Paths tab is accessible only when enabled, even if there is not content (expected behavior).
  • Finally, I verified that users are redirected to the Exercises tab if none of these features are accessible.

Sometimes right after activating/deactivating a feature it could still be the case that you are not shown/shown the feature or are wrongly redirected, but this is probably due to caching and after reloading the page a couple of times it works as expected.

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

🧹 Outside diff range and nitpick comments (4)
src/test/javascript/spec/component/overview/course-overview-guard.spec.ts (1)

1-170: Consider architectural improvements for test maintainability.

While the tests are comprehensive, consider these architectural improvements:

  1. Extract mock data to a separate test fixtures file to promote reuse and maintainability:
// course-overview.fixtures.ts
export const createMockExam = (overrides?: Partial<Exam>): Exam => ({
    id: 1,
    visibleDate: dayjs().subtract(1, 'days'),
    // ... other default properties
    ...overrides
});

export const createMockCourse = (overrides?: Partial<Course>): Course => ({
    // ... default properties
    ...overrides
});
  1. Consider using a test helper for common observable assertions to reduce boilerplate:
// test-helpers.ts
export const expectObservableValue = async <T>(
    observable: Observable<T>,
    expectedValue: T
): Promise<void> => {
    const value = await firstValueFrom(observable);
    expect(value).toBe(expectedValue);
};

This would simplify your tests:

it('should return true if course has lectures', async () => {
    await expectObservableValue(
        guard.handleReturn(mockCourse, 'lectures'),
        true
    );
});
src/main/webapp/app/overview/courses-routing.module.ts (2)

15-30: LGTM! Consider adding JSDoc documentation.

The introduction of CourseOverviewRoutePath enum is a good practice for centralizing route definitions. This improves maintainability and reduces the risk of typos in route paths.

Consider adding JSDoc documentation to describe the purpose of each route path:

 export enum CourseOverviewRoutePath {
+    /** Main dashboard view for the course */
     DASHBOARD = 'dashboard',
+    /** Course exercises overview */
     EXERCISES = 'exercises',
     // ... add documentation for other paths
 }

Line range hint 1-307: Consider a more granular guard strategy.

While the current implementation with CourseOverviewGuard works, consider splitting it into more specific guards (e.g., ExamGuard, CompetencyGuard) that extend a base CourseOverviewGuard. This would:

  1. Make the access control logic more maintainable
  2. Allow for route-specific access rules
  3. Follow the Single Responsibility Principle

Example approach:

abstract class BaseCourseOverviewGuard implements CanActivate {
    abstract checkAccess(course: Course): boolean;
    
    canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
        // Common guard logic
    }
}

@Injectable()
class ExamGuard extends BaseCourseOverviewGuard {
    checkAccess(course: Course): boolean {
        // Exam-specific access logic
    }
}

@Injectable()
class CompetencyGuard extends BaseCourseOverviewGuard {
    checkAccess(course: Course): boolean {
        // Competency-specific access logic
    }
}
src/main/webapp/app/overview/course-overview-guard.ts (1)

50-84: Refactor handleReturn to a class method

Defining handleReturn as a class method rather than an arrow function assigned to a property improves readability and aligns with the Angular style guide, which recommends declaring methods using the standard method syntax.

Apply this diff to refactor the method:

- handleReturn = (course?: Course, type?: string): Observable<boolean> => {
+ handleReturn(course?: Course, type?: string): Observable<boolean> {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2d4aba9 and 259e9dd.

📒 Files selected for processing (3)
  • src/main/webapp/app/overview/course-overview-guard.ts (1 hunks)
  • src/main/webapp/app/overview/courses-routing.module.ts (10 hunks)
  • src/test/javascript/spec/component/overview/course-overview-guard.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/main/webapp/app/overview/course-overview-guard.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/courses-routing.module.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-overview-guard.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}}

🔇 Additional comments (3)
src/test/javascript/spec/component/overview/course-overview-guard.spec.ts (1)

1-14: LGTM! Imports are well-organized and follow guidelines.

The imports are appropriate for the testing context and follow the best practices by avoiding full module imports.

src/main/webapp/app/overview/courses-routing.module.ts (2)

43-43: LGTM! Route paths consistently use the enum.

The conversion from string literals to enum values is complete and consistent across all routes. The lazy loading pattern is maintained, which is good for performance.

Also applies to: 47-47, 72-72, 146-146, 170-170, 179-179, 198-198, 207-207, 218-218, 228-228, 287-287, 299-299


185-185: Verify consistent guard implementation across routes.

The CourseOverviewGuard has been added to several routes, but we should verify:

  1. Whether all routes that need access control have the guard
  2. The order of guards when multiple are used (UserRouteAccessService, CourseOverviewGuard)

Let's analyze the guard implementation patterns:

Also applies to: 204-204, 215-215, 236-236, 261-261, 295-295

✅ Verification successful

Guard implementation patterns are consistent and correct

The verification shows consistent guard implementation patterns:

  1. Routes with only CourseOverviewGuard:

    • Competencies
    • FAQ
  2. Routes with both UserRouteAccessService and CourseOverviewGuard (in this order):

    • Dashboard
    • Learning Path
    • Exams
  3. Routes with only UserRouteAccessService:

    • Base course routes
    • Exercise details
    • Lecture details
    • Tutorial group details

The guard ordering is consistent where both guards are used (UserRouteAccessService first, then CourseOverviewGuard). The implementation aligns with access control requirements - routes that need course-specific checks use CourseOverviewGuard, while general course access is protected by UserRouteAccessService.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Analyze guard implementation patterns across routes

# Find all route definitions with guards
echo "Routes with guards:"
ast-grep --pattern 'canActivate: [$_]' 

# Find routes without guards
echo -e "\nRoutes without guards:"
ast-grep --pattern '{
  path: $_,
  $$$
}' | grep -v 'canActivate'

# Find guard ordering patterns
echo -e "\nGuard ordering patterns:"
ast-grep --pattern 'canActivate: [$$$]'

Length of output: 103633


Script:

#!/bin/bash
# Let's try a more focused search for routes with guards in the courses module
echo "Routes with guards in courses module:"
rg -A 1 "canActivate:" "src/main/webapp/app/overview/courses-routing.module.ts"

echo -e "\nGuard ordering patterns in courses module:"
rg -A 2 "canActivate: \[" "src/main/webapp/app/overview/courses-routing.module.ts"

Length of output: 4143

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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 259e9dd and 5a0ebcb.

📒 Files selected for processing (1)
  • src/test/javascript/spec/component/overview/course-overview-guard.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/test/javascript/spec/component/overview/course-overview-guard.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}}

🔇 Additional comments (1)
src/test/javascript/spec/component/overview/course-overview-guard.spec.ts (1)

21-30: 🛠️ Refactor suggestion

Enhance mock data completeness and type safety

The mock data setup needs improvement to ensure type safety and complete object initialization.

Apply this improvement to make the mock data more robust:

-const visibleRealExam = {
-    id: 1,
-    visibleDate: dayjs().subtract(1, 'days'),
-    startDate: dayjs().subtract(30, 'minutes'),
-    testExam: false,
-} as Exam;
+const visibleRealExam: Exam = {
+    id: 1,
+    visibleDate: dayjs().subtract(1, 'days'),
+    startDate: dayjs().subtract(30, 'minutes'),
+    endDate: dayjs().add(2, 'hours'),
+    testExam: false,
+    title: 'Test Exam',
+    course: undefined,
+    exerciseGroups: [],
+    workingTime: 120,
+    visible: true,
+    studentExams: [],
+};

-const lecture = new Lecture();
+const lecture: Lecture = {
+    id: 1,
+    title: 'Test Lecture',
+    description: 'Test Description',
+    attachments: [],
+    startDate: dayjs(),
+    endDate: dayjs().add(1, 'hour'),
+};

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 27, 2024
@cremertim cremertim removed deployment-error Added by deployment workflows if an error occured ready for review labels Nov 27, 2024
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: 0

🧹 Outside diff range and nitpick comments (2)
src/main/webapp/app/overview/course-overview-guard.ts (2)

35-35: Fix typo and improve comment clarity

The comment has a typo ("misses" should be "missing") and could be more descriptive.

-//we need to load the course from the server to check if the user has access to the requested route. The course in the cache might not be sufficient (e.g. misses exams or lectures)
+// Load fresh course data from the server to ensure accurate access control.
+// Cached course data might be incomplete (e.g., missing exams or lectures)

89-98: Optimize exam visibility check

Consider using Array methods for better readability and potentially better performance.

 hasVisibleExams(course?: Course): boolean {
-    if (course?.exams) {
-        for (const exam of course.exams) {
-            if (exam.visibleDate && dayjs(exam.visibleDate).isBefore(this.serverDateService.now())) {
-                return true;
-            }
-        }
-    }
-    return false;
+    return course?.exams?.some(
+        exam => exam.visibleDate && dayjs(exam.visibleDate).isBefore(this.serverDateService.now())
+    ) ?? false;
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5a0ebcb and 889be36.

📒 Files selected for processing (3)
  • src/main/webapp/app/overview/course-overview-guard.ts (1 hunks)
  • src/main/webapp/app/overview/courses-routing.module.ts (10 hunks)
  • src/test/javascript/spec/component/overview/course-overview-guard.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/webapp/app/overview/courses-routing.module.ts
  • src/test/javascript/spec/component/overview/course-overview-guard.spec.ts
🧰 Additional context used
📓 Path-based instructions (1)
src/main/webapp/app/overview/course-overview-guard.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 (5)
src/main/webapp/app/overview/course-overview-guard.ts (5)

1-19: LGTM! Clean implementation of dependency injection

The class follows Angular's best practices using the modern inject() function for dependency injection.


29-29: ⚠️ Potential issue

Add error handling and type safety

The current implementation has two potential issues:

  1. No error handling for HTTP request failures
  2. No validation for parsed courseId
 const courseIdNumber = parseInt(courseIdString, 10);
+if (isNaN(courseIdNumber)) {
+    return of(false);
+}

 return this.courseManagementService.findOneForDashboard(courseIdNumber).pipe(
     switchMap((res) => {
         if (res.body) {
             // Store course in cache
             this.courseStorageService.updateCourse(res.body);
         }
         // Flatten the result to return Observable<boolean> directly
         return this.handleReturn(this.courseStorageService.getCourse(courseIdNumber), path);
     }),
+    catchError((error) => {
+        console.error('Failed to load course:', error);
+        return of(false);
+    }),
 );

Also applies to: 36-45


56-56: ⚠️ Potential issue

Ensure lectures array has content before granting access

The current check doesn't verify if there are actually any lectures available.


84-84: ⚠️ Potential issue

Prevent navigation with undefined course ID

Navigation should be safeguarded against undefined course ID.


82-86: ⚠️ Potential issue

Prevent potential memory leaks from router navigation

The router navigation subscription should be properly handled to prevent memory leaks.

 if (!hasAccess) {
-    this.router.navigate([`/courses/${course?.id}/exercises`]);
+    if (course?.id) {
+        this.router.navigate([`/courses/${course.id}/exercises`])
+            .catch(error => console.error('Navigation failed:', error));
+    }
 }

Likely invalid or redundant comment.

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 29, 2024
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 ts3, works as expected

Copy link

@eylulnc eylulnc 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 TS3 works as expected

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.