Skip to content

Implemented Short Course APIs#7

Merged
mblebelo merged 2 commits into
mainfrom
benny/ShortCourseAPIs
Dec 17, 2025
Merged

Implemented Short Course APIs#7
mblebelo merged 2 commits into
mainfrom
benny/ShortCourseAPIs

Conversation

@blebelo
Copy link
Copy Markdown
Owner

@blebelo blebelo commented Dec 17, 2025

Summary by CodeRabbit

  • New Features
    • Short course management system for creating, updating, and managing courses
    • Retrieve courses by course code
    • Manage course application availability (open, close, and reopen applications)
    • View all currently open courses
    • Track student enrollment within courses

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Dec 17, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
moipone Ready Ready Preview, Comment Dec 17, 2025 10:23pm

@blebelo blebelo temporarily deployed to benny/ShortCourseAPIs - Moipone PS API PR #7 December 17, 2025 22:23 — with Render Destroyed
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 17, 2025

Walkthrough

Introduces a complete application service layer for short course management, consisting of a data transfer object (DTO) for ShortCourse with AutoMapper mapping, a service interface defining CRUD and domain-specific operations, and a concrete implementation handling validation, persistence, and error management.

Changes

Cohort / File(s) Change Summary
ShortCourse Application Service Layer
aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/Dto/ShortCourseDto.cs, aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/IShortCourseAppService.cs, aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/ShortCourseAppService.cs
New DTO with core properties (Title, Description, Capacity, Requirements, Code, IsActive, StartDate, Duration) and enrolled students collection; service interface extending IAsyncCrudAppService with methods for course lookup, application state management (Open/Close/Reopen), and filtering; concrete service implementation with CRUD operations including validation, error handling, and business logic for managing course applications and active course retrieval.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Review consistency of error handling and logging patterns across CRUD methods
  • Validate AutoMapper configuration and DTO property mappings
  • Verify repository query correctness and data retrieval logic
  • Ensure input validation is applied uniformly in Create, Update, and Delete operations
  • Check that IsActive state transitions in Open/Close/Reopen methods align with business requirements

Poem

🐰 A new service hops into place,
With DTOs and interfaces with grace,
CRUD operations all aligned,
Courses managed, well-designed!
Short courses now have their space! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Implemented Short Course APIs' accurately summarizes the main changes, which introduce a complete API implementation for short courses including DTO, service interface, and service implementation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch benny/ShortCourseAPIs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@blebelo blebelo requested a review from mblebelo December 17, 2025 22:23
Copy link
Copy Markdown
Collaborator

@mblebelo mblebelo left a comment

Choose a reason for hiding this comment

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

Code RabbitAI: All checks passed and conversations resolved ✅

@mblebelo mblebelo merged commit cd8d65f into main Dec 17, 2025
8 of 9 checks passed
Copy link
Copy Markdown
Collaborator

@mblebelo mblebelo left a comment

Choose a reason for hiding this comment

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

Code RabbitAI: All checks passed and conversations resolved ✅

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (6)
aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/Dto/ShortCourseDto.cs (1)

26-26: Remove commented code or convert to a TODO.

If the Applications property is planned for future implementation, convert the comment to a proper TODO. Otherwise, remove it to keep the codebase clean.

aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/ShortCourseAppService.cs (5)

56-81: Consider eager loading for EnrolledStudents to prevent N+1 queries.

If the EnrolledStudents collection should be included in the result, add .Include() to avoid N+1 query problems when AutoMapper accesses the navigation property.

Apply this diff if EnrolledStudents should be loaded:

+using Microsoft.EntityFrameworkCore;
+
 // ... in GetAllAsync method:
-                var query = Repository.GetAll();
+                var query = Repository.GetAll()
+                    .Include(sc => sc.EnrolledStudents);

Alternatively, if EnrolledStudents should not be loaded here, ensure the AutoMapper configuration ignores this property or the DTO property is nullable/initialized.


95-103: Redundant null check after repository GetAsync.

The ABP Repository.GetAsync() method throws an exception if the entity is not found, so the null check on lines 97-103 is unreachable. You can safely remove this check or use FirstOrDefaultAsync() if you need to handle the null case explicitly.

Apply this diff to remove the redundant check:

                 var shortCourse = await _shortCourseRepository.GetAsync(input.Id);
 
-                if (shortCourse == null)
-                {
-                    throw new UserFriendlyException(
-                        $"Short course with ID {input.Id} not found.",
-                        Abp.Logging.LogSeverity.Warn
-                    );
-                }
-
                 return ObjectMapper.Map<ShortCourseDto>(shortCourse);

174-182: Redundant null check after repository GetAsync.

Similar to the GetAsync method, the null check here is redundant since Repository.GetAsync() throws if the entity doesn't exist.

Apply this diff:

                 var shortCourse = await _shortCourseRepository.GetAsync(input.Id);
 
-                if (shortCourse == null)
-                {
-                    throw new UserFriendlyException(
-                        $"Short course with ID {input.Id} not found.",
-                        Abp.Logging.LogSeverity.Warn
-                    );
-                }
-
                 await _shortCourseRepository.DeleteAsync(input.Id);

212-216: Consider using SingleOrDefaultAsync if Code is unique.

If the Code property is unique (enforced by database constraint or domain logic), use SingleOrDefaultAsync() instead of FirstOrDefaultAsync() to catch data integrity issues where multiple courses share the same code.

Apply this diff:

-                var shortCourse = await AsyncQueryableExecuter.FirstOrDefaultAsync(
+                var shortCourse = await AsyncQueryableExecuter.SingleOrDefaultAsync(
                     _shortCourseRepository
                         .GetAll()
                         .Where(sc => sc.Code == code)
                 );

365-386: Consider adding pagination to GetOpenCoursesAsync.

The method returns all active courses without pagination, which could cause performance issues if the number of active courses grows large. Consider accepting a paging parameter or adding a reasonable limit.

Apply this diff to add pagination:

-        public async Task<List<ShortCourseDto>> GetOpenCoursesAsync()
+        public async Task<PagedResultDto<ShortCourseDto>> GetOpenCoursesAsync(PagedAndSortedResultRequestDto input)
         {
             try
             {
+                var query = _shortCourseRepository
+                    .GetAll()
+                    .Where(sc => sc.IsActive);
+                
+                var totalCount = await AsyncQueryableExecuter.CountAsync(query);
+                
                 var shortCourses = await AsyncQueryableExecuter.ToListAsync(
-                    _shortCourseRepository
-                        .GetAll()
-                        .Where(sc => sc.IsActive)
+                    query
                         .OrderBy(sc => sc.StartDate)
+                        .Skip(input.SkipCount)
+                        .Take(input.MaxResultCount)
                 );
 
-                return ObjectMapper.Map<List<ShortCourseDto>>(shortCourses);
+                var result = ObjectMapper.Map<List<ShortCourseDto>>(shortCourses);
+                return new PagedResultDto<ShortCourseDto>(totalCount, result);
             }

Note: This would require updating the interface signature as well.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfabff0 and 9c410c6.

📒 Files selected for processing (3)
  • aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/Dto/ShortCourseDto.cs (1 hunks)
  • aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/IShortCourseAppService.cs (1 hunks)
  • aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/ShortCourseAppService.cs (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: blebelo
Repo: blebelo/moipone PR: 6
File: aspnet-core/src/Moipone.PublicSite.Application/Students/Dto/StudentDto.cs:13-26
Timestamp: 2025-12-17T20:15:26.321Z
Learning: In the Moipone.PublicSite project, the [Required] attributes on domain entities like Student are used for database schema generation and object creation validation, not for DTO-level validation. This allows DTOs to remain flexible with nullable properties for API operations while the domain layer enforces data integrity.
📚 Learning: 2025-12-17T20:11:46.178Z
Learnt from: blebelo
Repo: blebelo/moipone PR: 6
File: aspnet-core/src/Moipone.PublicSite.Application/Students/Dto/StudentDto.cs:13-26
Timestamp: 2025-12-17T20:11:46.178Z
Learning: In the Moipone.PublicSite project, StudentDto uses nullable properties for all fields to support partial updates and single field updates in the update operations, even though the underlying Student domain entity has [Required] attributes on many fields.

Applied to files:

  • aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/Dto/ShortCourseDto.cs
📚 Learning: 2025-12-17T20:15:26.321Z
Learnt from: blebelo
Repo: blebelo/moipone PR: 6
File: aspnet-core/src/Moipone.PublicSite.Application/Students/Dto/StudentDto.cs:13-26
Timestamp: 2025-12-17T20:15:26.321Z
Learning: In ASP.NET Core projects with a layered architecture, avoid using [Required] on DTOs for API contracts; reserve [Required] for domain entities that participate in persistence. DTOs can have nullable properties to support optional fields in API operations, while the domain layer enforces data integrity. Ensure the domain models (e.g., Student) have appropriate [Required] attributes, and mapping from DTOs to domain should validate required fields before domain creation.

Applied to files:

  • aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/Dto/ShortCourseDto.cs
🪛 GitHub Check: Build Stage
aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/Dto/ShortCourseDto.cs

[warning] 18-18:
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.


[warning] 17-17:
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.


[warning] 15-15:
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.


[warning] 14-14:
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test Stage
  • GitHub Check: auto-pr
🔇 Additional comments (4)
aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/IShortCourseAppService.cs (1)

9-16: LGTM! Clean interface definition.

The interface properly extends IAsyncCrudAppService and adds domain-specific operations. Method signatures are clear and consistent.

aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/ShortCourseAppService.cs (3)

20-24: LGTM! Standard constructor pattern.

The constructor follows ABP conventions by accepting the repository and passing it to the base class.


26-54: Add validation attributes to ShortCourseDto to match domain entity constraints.

The ShortCourse domain entity already enforces validation via [Required], [StringLength], [Range], and [RegularExpression] attributes. However, ShortCourseDto lacks these validation attributes—all properties are nullable with no constraints. ABP will not validate the DTO automatically without these attributes, allowing invalid data to be mapped and persisted. Add matching validation attributes to the DTO properties to ensure validation occurs at the service layer before mapping to the domain entity.

⛔ Skipped due to learnings
Learnt from: blebelo
Repo: blebelo/moipone PR: 6
File: aspnet-core/src/Moipone.PublicSite.Application/Students/Dto/StudentDto.cs:13-26
Timestamp: 2025-12-17T20:15:26.321Z
Learning: In the Moipone.PublicSite project, the [Required] attributes on domain entities like Student are used for database schema generation and object creation validation, not for DTO-level validation. This allows DTOs to remain flexible with nullable properties for API operations while the domain layer enforces data integrity.
Learnt from: blebelo
Repo: blebelo/moipone PR: 6
File: aspnet-core/src/Moipone.PublicSite.Application/Students/Dto/StudentDto.cs:13-26
Timestamp: 2025-12-17T20:11:46.178Z
Learning: In the Moipone.PublicSite project, StudentDto uses nullable properties for all fields to support partial updates and single field updates in the update operations, even though the underlying Student domain entity has [Required] attributes on many fields.

141-144: Partial update concern: Nullable DTO properties may unintentionally set fields to null.

ObjectMapper.Map(input, shortCourse) will map all properties from the DTO, including null values. Since ShortCourseDto has nullable properties, any field not provided in the input will be mapped as null, overwriting existing entity values. This pattern exists across StudentAppService and AddressAppService with the same issue. Configure AutoMapper to skip null values using .ForAllMembers(opt => opt.Condition((src, dest, propValue, context) => propValue != null)) or handle partial updates explicitly in the Update method.

⛔ Skipped due to learnings
Learnt from: blebelo
Repo: blebelo/moipone PR: 6
File: aspnet-core/src/Moipone.PublicSite.Application/Students/Dto/StudentDto.cs:13-26
Timestamp: 2025-12-17T20:11:46.178Z
Learning: In the Moipone.PublicSite project, StudentDto uses nullable properties for all fields to support partial updates and single field updates in the update operations, even though the underlying Student domain entity has [Required] attributes on many fields.
Learnt from: blebelo
Repo: blebelo/moipone PR: 6
File: aspnet-core/src/Moipone.PublicSite.Application/Students/Dto/StudentDto.cs:13-26
Timestamp: 2025-12-17T20:15:26.321Z
Learning: In the Moipone.PublicSite project, the [Required] attributes on domain entities like Student are used for database schema generation and object creation validation, not for DTO-level validation. This allows DTOs to remain flexible with nullable properties for API operations while the domain layer enforces data integrity.

Comment on lines +14 to +18
public string? Title { get; set; }
public string? Description { get; set; }
public int? Capacity { get; set; }
public string? Requirements { get; set; }
public string? Code { get; set; }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Enable nullable reference context to resolve compiler warnings.

The static analysis correctly flags that nullable annotations require a #nullable context. Add #nullable enable at the top of the file (after usings) to properly enable nullable reference types.

Apply this diff:

 using System.Collections.Generic;
 
 namespace Moipone.PublicSite.ShortCourses.Dto
 {
+#nullable enable
     [AutoMap(typeof(ShortCourse))]
     public class ShortCourseDto : EntityDto<Guid>

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

🧰 Tools
🪛 GitHub Check: Build Stage

[warning] 18-18:
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.


[warning] 17-17:
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.


[warning] 15-15:
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.


[warning] 14-14:
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

🤖 Prompt for AI Agents
In
aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/Dto/ShortCourseDto.cs
around lines 14 to 18, nullable reference annotations are used but the file
lacks a nullable context; add a "#nullable enable" directive near the top of the
file (immediately after the using statements) so the compiler recognizes the
nullable annotations and clears the related warnings.

#endregion

#region Enrollment Data
public ICollection<StudentDto> EnrolledStudents { get; set; }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Initialize the EnrolledStudents collection to prevent NullReferenceException.

The non-nullable EnrolledStudents property is not initialized, which will cause a NullReferenceException when accessed if AutoMapper doesn't populate it from the source entity.

Apply this diff to initialize the collection:

-        public ICollection<StudentDto> EnrolledStudents { get; set; }
+        public ICollection<StudentDto> EnrolledStudents { get; set; } = new List<StudentDto>();

Alternatively, if the property should support null values when no students are enrolled, make it nullable:

-        public ICollection<StudentDto> EnrolledStudents { get; set; }
+        public ICollection<StudentDto>? EnrolledStudents { get; set; }
📝 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
public ICollection<StudentDto> EnrolledStudents { get; set; }
public ICollection<StudentDto> EnrolledStudents { get; set; } = new List<StudentDto>();
Suggested change
public ICollection<StudentDto> EnrolledStudents { get; set; }
public ICollection<StudentDto>? EnrolledStudents { get; set; }
🤖 Prompt for AI Agents
aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/Dto/ShortCourseDto.cs
around line 25: the non-nullable EnrolledStudents property is not initialized
which can cause NullReferenceException; either initialize it to an empty
collection (e.g., assign new List<StudentDto>() in the property declaration or
constructor) or change its type to nullable (ICollection<StudentDto>?) if null
is an acceptable state—update the property accordingly and ensure any callers
handle the chosen non-null or nullable contract.

{
Logger.Error("Error creating short course", ex);
throw new UserFriendlyException(
$"Could not create Short Course. Error: {ex.Message}",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid exposing internal exception details to users.

Including ex.Message in the UserFriendlyException may expose sensitive internal implementation details (e.g., database connection strings, stack traces). Consider using a generic error message instead.

Apply this diff:

                 Logger.Error("Error creating short course", ex);
                 throw new UserFriendlyException(
-                    $"Could not create Short Course. Error: {ex.Message}",
+                    "Could not create Short Course. Please try again or contact support.",
                     Abp.Logging.LogSeverity.Error
                 );
📝 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
$"Could not create Short Course. Error: {ex.Message}",
Logger.Error("Error creating short course", ex);
throw new UserFriendlyException(
"Could not create Short Course. Please try again or contact support.",
Abp.Logging.LogSeverity.Error
);
🤖 Prompt for AI Agents
In
aspnet-core/src/Moipone.PublicSite.Application/ShortCourses/ShortCourseAppService.cs
around line 50, the code puts ex.Message into a UserFriendlyException exposing
internal error details; change it to throw a UserFriendlyException with a
generic, non-sensitive message (e.g., "An error occurred while creating the
short course. Please try again or contact support.") and move the full exception
details into server-side logging (e.g., Logger.LogError(ex, "...")) so the
exception is recorded for diagnostics but not shown to the user.

Comment on lines +242 to +281
public async Task<ShortCourseDto> OpenApplicationsAsync(Guid id)
{
try
{
if (id == Guid.Empty)
{
throw new UserFriendlyException(
"Invalid short course ID.",
Abp.Logging.LogSeverity.Warn
);
}

var shortCourse = await _shortCourseRepository.GetAsync(id);

if (shortCourse == null)
{
throw new UserFriendlyException(
$"Short course with ID {id} not found.",
Abp.Logging.LogSeverity.Warn
);
}

shortCourse.IsActive = true;
var updatedShortCourse = await _shortCourseRepository.UpdateAsync(shortCourse);

return ObjectMapper.Map<ShortCourseDto>(updatedShortCourse);
}
catch (UserFriendlyException)
{
throw;
}
catch (Exception ex)
{
Logger.Error($"Error opening applications for short course with ID {id}", ex);
throw new UserFriendlyException(
$"Could not open applications. Error: {ex.Message}",
Abp.Logging.LogSeverity.Error
);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

OpenApplicationsAsync and ReopenApplicationsAsync are identical.

Both methods perform exactly the same operation (setting IsActive = true). This is code duplication that should be addressed.

Option 1: If the methods should have identical behavior, consolidate them:

         public async Task<ShortCourseDto> OpenApplicationsAsync(Guid id)
         {
-            try
-            {
-                if (id == Guid.Empty)
-                {
-                    throw new UserFriendlyException(
-                        "Invalid short course ID.",
-                        Abp.Logging.LogSeverity.Warn
-                    );
-                }
-
-                var shortCourse = await _shortCourseRepository.GetAsync(id);
-
-                if (shortCourse == null)
-                {
-                    throw new UserFriendlyException(
-                        $"Short course with ID {id} not found.",
-                        Abp.Logging.LogSeverity.Warn
-                    );
-                }
-
-                shortCourse.IsActive = true;
-                var updatedShortCourse = await _shortCourseRepository.UpdateAsync(shortCourse);
-
-                return ObjectMapper.Map<ShortCourseDto>(updatedShortCourse);
-            }
-            catch (UserFriendlyException)
-            {
-                throw;
-            }
-            catch (Exception ex)
-            {
-                Logger.Error($"Error opening applications for short course with ID {id}", ex);
-                throw new UserFriendlyException(
-                    $"Could not open applications. Error: {ex.Message}",
-                    Abp.Logging.LogSeverity.Error
-                );
-            }
+            return await SetApplicationStatusAsync(id, true, "open");
         }

         public async Task<ShortCourseDto> ReopenApplicationsAsync(Guid id)
         {
-            // ... identical code ...
+            return await SetApplicationStatusAsync(id, true, "reopen");
         }
+        
+        private async Task<ShortCourseDto> SetApplicationStatusAsync(Guid id, bool isActive, string action)
+        {
+            try
+            {
+                if (id == Guid.Empty)
+                {
+                    throw new UserFriendlyException(
+                        "Invalid short course ID.",
+                        Abp.Logging.LogSeverity.Warn
+                    );
+                }
+
+                var shortCourse = await _shortCourseRepository.GetAsync(id);
+                shortCourse.IsActive = isActive;
+                var updatedShortCourse = await _shortCourseRepository.UpdateAsync(shortCourse);
+
+                return ObjectMapper.Map<ShortCourseDto>(updatedShortCourse);
+            }
+            catch (UserFriendlyException)
+            {
+                throw;
+            }
+            catch (Exception ex)
+            {
+                Logger.Error($"Error {action}ing applications for short course with ID {id}", ex);
+                throw new UserFriendlyException(
+                    $"Could not {action} applications.",
+                    Abp.Logging.LogSeverity.Error
+                );
+            }
+        }

Option 2: If they should behave differently (e.g., tracking state history, business rules), add domain methods to the ShortCourse entity:

// In ShortCourse entity:
public void OpenApplications() { /* domain logic */ }
public void ReopenApplications() { /* different domain logic */ }

Also applies to: 324-363

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants