-
Notifications
You must be signed in to change notification settings - Fork 314
Programming exercises
: Exchange of Artemis programming exercises via CodeAbility Sharing Platform
#10989
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
base: develop
Are you sure you want to change the base?
Conversation
End-to-End (E2E) Test Results Summary |
## Walkthrough
This change introduces a comprehensive integration between Artemis and the CodeAbility Sharing Platform, gated by a new "sharing" Spring profile. It adds backend services, REST controllers, DTOs, and health checks for secure import/export of programming exercises, along with Angular components, services, routes, and UI elements for instructors. Extensive documentation, localization, and automated tests are included.
## Changes
| File(s) / Group | Change Summary |
|---------------------------------------------------------|---------------|
| `build.gradle`, `docker/...env` | Added Sharing Platform dependency; enabled "sharing" profile in Docker env. |
| `docs/admin/extension-services.rst`, `docs/admin/setup/sharing.rst`, `docs/index.rst`, `docs/user/sharing.rst` | Added/updated documentation for Sharing Platform integration and user guide. |
| `src/main/java/.../config/Constants.java`, `ProfileService.java` | Added `PROFILE_SHARING` and related profile-check methods. |
| `src/main/java/.../config/RestTemplateConfiguration.java` | Added `sharingRestTemplate` bean for Sharing Platform. |
| `src/main/java/.../config/SecurityConfiguration.java` | Allowed unauthenticated access to `/api/programming/sharing/**` and `/api/core/sharing/**` endpoints. |
| `src/main/java/.../core/dto/SharingInfoDTO.java` | New DTO record for sharing info, with checksum validation. |
| `src/main/java/.../core/web/SharingSupportResource.java` | New REST controller for sharing config exchange. |
| `src/main/java/.../programming/service/ProgrammingExerciseImportFromFileService.java` | Added sharing-specific import logic and method overload. |
| `src/main/java/.../programming/service/sharing/` | Added new sharing services: `ExerciseSharingService`, `ProgrammingExerciseImportFromSharingService`, `SharingConnectorService`, `SharingException`, `SharingHealthIndicator`, `SharingMultipartZipFile`, `SharingSetupInfo`. |
| `src/main/java/.../programming/web/ExerciseSharingResource.java` | New REST controller for sharing import/export endpoints. |
| `src/main/webapp/app/...` (multiple files) | Added Angular sharing service, routes, components, UI logic, SCSS, and model for sharing; integrated sharing import/export into programming exercise management. |
| `src/main/webapp/i18n/.../programmingExercise.json`, `sharing.json` | Added localization keys for sharing in EN/DE. |
| `src/test/java/.../programming/service/sharing/` | Added integration and unit tests for sharing backend, DTOs, mock provider, and health checks. |
| `src/test/resources/.../sampleBasket.json` | Added sample sharing basket test data. |
| `src/test/javascript/spec/helpers/.../programming-exercise-creation-config-mock.ts` | Added sharing flag to test config mock. |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant Instructor
participant Artemis_UI
participant Artemis_Backend
participant SharingPlatform
Instructor->>Artemis_UI: Click "Export to Sharing"
Artemis_UI->>Artemis_Backend: POST /api/programming/sharing/export/{exerciseId}
Artemis_Backend->>SharingPlatform: (if needed) Register/validate API key
Artemis_Backend-->>Artemis_UI: Return sharing platform redirect URL
Artemis_UI->>SharingPlatform: Redirect user for metadata/namespace selection
SharingPlatform->>Artemis_Backend: GET exported exercise via callback URL
Artemis_Backend-->>SharingPlatform: Return exercise ZIP sequenceDiagram
participant Instructor
participant SharingPlatform
participant Artemis_UI
participant Artemis_Backend
Instructor->>SharingPlatform: Search/select exercise to import
SharingPlatform->>Artemis_UI: Redirect with basketToken, apiBaseURL, etc.
Artemis_UI->>Artemis_Backend: GET /api/programming/sharing/import/basket
Artemis_Backend->>SharingPlatform: GET basket info using token
SharingPlatform-->>Artemis_Backend: Return basket data
Artemis_Backend-->>Artemis_UI: Return basket data
Instructor->>Artemis_UI: Select exercise and course, trigger import
Artemis_UI->>Artemis_Backend: POST /api/programming/sharing/setup-import
Artemis_Backend->>SharingPlatform: Download ZIP for exercise
Artemis_Backend->>Artemis_Backend: Import exercise into selected course
Artemis_Backend-->>Artemis_UI: Return imported exercise
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
Suggested reviewers
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 44
🧹 Nitpick comments (63)
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java (3)
9-11
: Improve JavaDoc for better clarity.The JavaDoc comment could be more descriptive and follow standard conventions.
Consider this improvement:
-/** - * the sharing info, wrapping the original sharing Info from the sharing platform and adding course and exercise info. - */ +/** + * Data wrapper that combines sharing information from the sharing platform with additional context. + * + * @param exercise the programming exercise to be shared or imported + * @param course the course context for the exercise + * @param sharingInfo the original sharing information from the sharing platform + */
9-11
: Improve JavaDoc formatting and add parameter documentation.The JavaDoc comment should start with a capital letter and include parameter documentation for the record components.
/** - * the sharing info, wrapping the original sharing Info from the sharing platform and adding course and exercise info. + * The sharing info, wrapping the original sharing info from the sharing platform and adding course and exercise info. + * + * @param exercise the programming exercise to be shared + * @param course the course containing the exercise + * @param sharingInfo the original sharing information from the sharing platform */
13-13
: Consider whether entities should be included in this data wrapper.The record contains
ProgrammingExercise
andCourse
entities, which goes against the coding guideline "dtos:{java_records,no_entities,min_data,single_resp}". Since this appears to be a service-layer data holder rather than a REST DTO, consider whether this design is intentional or if separate DTOs should be used instead.src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java (5)
16-31
: Improve JavaDoc comments for accuracy.The JavaDoc comments are misleading - they all mention "connection failures" but the constructors are generic.
Consider this improvement:
- /** - * Creates a SharingException for connection failures. - */ + /** + * Creates a SharingException with the specified message. + * + * @param message the detail message + */ public SharingException(String message) { super(message); } - /** - * Creates a SharingException for connection failures. - * - * @param cause The underlying cause - */ + /** + * Creates a SharingException with the specified message and cause. + * + * @param message the detail message + * @param cause the underlying cause + */ public SharingException(String message, Throwable cause) { super(message, cause); }
16-31
: Fix misleading and repetitive JavaDoc comments.The JavaDoc comments are repetitive and misleading. The second constructor is not specifically for "connection failures" as stated.
/** - * Creates a SharingException for connection failures. + * Creates a SharingException with a message. + * + * @param message the detail message */ public SharingException(String message) { super(message); } /** - * Creates a SharingException for connection failures. + * Creates a SharingException with a message and cause. * + * @param message the detail message * @param cause The underlying cause */
39-41
: Consider a more generic factory method name.The
connectionError
method is specific to connection errors, but the class might be used for other sharing-related errors as well.Consider renaming for better reusability:
-public static SharingException connectionError(String endpoint, Throwable cause) { +public static SharingException withEndpoint(String endpoint, Throwable cause) { return new SharingException("Failed to connect to sharing platform at " + endpoint, cause); }This makes the method more generic while maintaining its current functionality.
16-21
: Improve JavaDoc documentation for constructor.The JavaDoc comment says "Creates a SharingException for connection failures" but this constructor is generic and can be used for any sharing-related error, not just connection failures.
/** - * Creates a SharingException for connection failures. + * Creates a SharingException with the specified message. + * + * @param message the detail message */
23-30
: Improve JavaDoc documentation for constructor with cause.Similar to the previous constructor, the JavaDoc is too specific about "connection failures" when this constructor can be used for any sharing exception.
/** - * Creates a SharingException for connection failures. + * Creates a SharingException with the specified message and cause. * + * @param message the detail message * @param cause The underlying cause */src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java (5)
14-16
: Improve JavaDoc documentation format.The JavaDoc comment should follow proper formatting conventions with a capital letter and proper punctuation.
-/** - * health indicator that shows the status of the sharing platform connector. - */ +/** + * Health indicator that shows the status of the sharing platform connector. + */
27-30
: Remove unnecessary super() call.The explicit
super()
call is redundant in Java constructors when calling the no-argument constructor of the parent class.public SharingHealthIndicator(SharingConnectorService sharingConnectorService) { - super(); this.sharingConnectorService = sharingConnectorService; }
32-35
: Improve JavaDoc documentation format and grammar.The JavaDoc has formatting and grammatical issues that should be corrected.
-/** - * returns the main health status (up/down or unknown if config request from sharing platform is to long ago), together - * with a list of the 10 last log events for the sharing connector. - */ +/** + * Returns the main health status (up/down or unknown if config request from sharing platform is too long ago), together + * with a list of the 10 most recent log events for the sharing connector. + */
43-43
: Consider making the timeout configurable.The 11-minute threshold is hardcoded, which makes it difficult to adjust for different environments or requirements without code changes.
Consider extracting this to a configurable property:
+@Value("${artemis.sharing.health-timeout-minutes:11}") +private int healthTimeoutMinutes; -else if (lastHealthStati.getLastConnect().isBefore(Instant.now().minus(11, ChronoUnit.MINUTES))) { +else if (lastHealthStati.getLastConnect().isBefore(Instant.now().minus(healthTimeoutMinutes, ChronoUnit.MINUTES))) {
50-55
: Optimize iteration and formatting.The current approach with reverse iteration and string formatting could be simplified and made more efficient.
-for (int i = lastHealthStati.size() - 1; i >= 0; i--) { - SharingConnectorService.HealthStatus hs = lastHealthStati.get(i); +int counter = lastHealthStati.size(); +for (int i = lastHealthStati.size() - 1; i >= 0; i--) { + SharingConnectorService.HealthStatus hs = lastHealthStati.get(i); ZonedDateTime zonedTimestamp = hs.getTimeStamp().atZone(UTC); String timeStamp = TIME_STAMP_FORMATTER.format(zonedTimestamp); - health.withDetail(String.format("%3d: %s", i + 1, timeStamp), hs.getStatusMessage()); + health.withDetail(String.format("%3d: %s", counter--, timeStamp), hs.getStatusMessage()); }src/main/webapp/app/sharing/sharing.model.ts (3)
13-13
: Fix typo in comment.There's a spelling error in the comment.
- /** checksum fo apiBaseURL and returnURL */ + /** checksum for apiBaseURL and returnURL */
26-31
: Consider including checksum in clear method.The
clear()
method resets most properties but doesn't reset thechecksum
property, which might lead to inconsistent state.public clear(): void { this.basketToken = ''; this.selectedExercise = 0; this.returnURL = ''; this.apiBaseURL = ''; + this.checksum = ''; }
50-54
: Consider using readonly for interface properties.The
ShoppingBasket
interface properties could be marked as readonly to prevent accidental mutations, especially for data coming from an external API.export interface ShoppingBasket { - exerciseInfo: Array<SearchResultDTO>; - userInfo: UserInfo; - tokenValidUntil: Date; + readonly exerciseInfo: readonly SearchResultDTO[]; + readonly userInfo: UserInfo; + readonly tokenValidUntil: Date; }src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java (3)
29-36
: Improve test method name and add setup verification.The test method name could be more descriptive, and the test should verify the health indicator setup before asserting the result.
@Test -void healthUp() throws Exception { +void shouldReportHealthUpWhenSharingPlatformIsConnected() throws Exception { + // given sharingPlatformMockProvider.mockStatus(true); + // when final Health health = sharingHealthIndicator.health(); + + // then assertThat(health.getStatus()).isEqualTo(Status.UP); }
38-49
: Improve test method name and add intermediate assertions.The test method name should be more descriptive, and the test could benefit from intermediate assertions to make the test more robust.
@Test -void testMaxStatusInfo() throws Exception { +void shouldLimitHealthHistorySizeToConfiguredMaximum() throws Exception { + // given sharingPlatformMockProvider.mockStatus(true); SharingConnectorService.HealthStatusWithHistory lastHealthStati = sharingConnectorService.getLastHealthStati(); + int initialSize = lastHealthStati.size(); + // when - adding more entries than the limit for (int i = 0; i < SharingConnectorService.HEALTH_HISTORY_LIMIT * 2; i++) { lastHealthStati.add(new SharingConnectorService.HealthStatus("Just Testing")); assertThat(lastHealthStati).size().isLessThanOrEqualTo(SharingConnectorService.HEALTH_HISTORY_LIMIT); } + + // then assertThat(lastHealthStati).size().isEqualTo(SharingConnectorService.HEALTH_HISTORY_LIMIT); }
51-57
: Improve test method name and structure.The test method name should be more descriptive and follow the established testing pattern.
@Test -void healthDown() throws Exception { +void shouldReportHealthDownWhenSharingPlatformIsDisconnected() throws Exception { + // given sharingPlatformMockProvider.mockStatus(false); + // when final Health health = sharingHealthIndicator.health(); + + // then assertThat(health.getStatus()).isEqualTo(Status.DOWN); }src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts (1)
59-59
: Remove commented code.The commented line should be removed to maintain code cleanliness.
- // const sharingWindow = window.open(redirectURL, 'sharing');
src/main/webapp/app/sharing/sharing.component.ts (1)
154-155
: Remove redundant return statement.The return statement in the navigation promise is unnecessary since the method doesn't return anything.
.then((nav) => { - return true; // true if navigation is successful + // Navigation completed });src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java (2)
99-104
: Improve test method naming to follow BDD conventions.Test method names should clearly describe the scenario being tested using a more descriptive pattern.
Consider renaming tests to be more descriptive:
- public void connectRequestFromSharingPlatform() throws Exception { + public void shouldReturnConfigurationWhenValidApiKeyProvided() throws Exception { - public void connectRequestFromSharingPlatformWithWrongApiKey() throws Exception { + public void shouldReturnUnauthorizedWhenInvalidApiKeyProvided() throws Exception { - public void connectRequestFromSharingPlatformWithMissingApiKey() throws Exception { + public void shouldReturnUnauthorizedWhenApiKeyMissing() throws Exception {Also applies to: 111-115, 122-127, 134-138, 145-149
103-103
: Remove unused variable or add assertions.The
content
variable is extracted but never used, suggesting missing assertions or dead code.Either add assertions on the content:
String content = result.getResponse().getContentAsString(); + assertThat(content).isNotEmpty(); + // Add specific content assertionsOr remove the unused variable:
- String content = result.getResponse().getContentAsString();
src/main/webapp/app/sharing/sharing.component.html (2)
48-48
: Move complex template expression to component method.The expression
getTokenExpiryDate().toLocaleString()
should be moved to a component method for better testability and performance.Create a getter method in the component:
get formattedExpiryDate(): string { return this.getTokenExpiryDate().toLocaleString(); }Then update the template:
-<span jhiTranslate="artemisApp.sharing.expiresAt" [translateValues]="{ expirationDate: getTokenExpiryDate().toLocaleString() }">expires at: </span> +<span jhiTranslate="artemisApp.sharing.expiresAt" [translateValues]="{ expirationDate: formattedExpiryDate }">expires at: </span>
3-3
: Remove inline styles in favor of CSS classes.Inline styles should be avoided in favor of CSS classes for better maintainability and consistency with theming guidelines.
Move inline styles to the component's CSS file:
.sharing-table { border-spacing: 50px 0px; border-collapse: separate; } .vertical-align-top { vertical-align: top; } .course-color-indicator { width: 15px; height: 20px; }Then update the template:
-<table style="border-spacing: 50px 0px; border-collapse: separate"> +<table class="sharing-table">Also applies to: 83-83
src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java (3)
124-124
: Remove unused variable declaration.The variable
oldShortName
is declared but never used, which violates the coding guidelines about avoiding unused variables.-var oldShortName = getProgrammingExerciseFromDetailsFile(importExerciseDir).getShortName();
If this variable is needed for future functionality, add a TODO comment explaining its purpose.
97-98
: Update method documentation to reflect the new parameter.The Javadoc should be updated to properly document the new
isImportFromSharing
parameter./** * Imports a programming exercise from an uploaded zip file that has previously been downloaded from an Artemis instance. * It first extracts the contents of the zip file, then creates a programming exercise (same process as creating a new one), * then deletes the template content initially pushed to the repositories and copies over the extracted content * * @param originalProgrammingExercise the programming exercise that should be imported * @param zipFile the zip file that contains the exercise * @param course the course to which the exercise should be added * @param user the user initiating the import - * @param isImportFromSharing flag whether file import (false) of sharing import + * @param isImportFromSharing flag indicating whether this is a sharing platform import (true) or regular file import (false) * @return the imported programming exercise **/
175-181
: Improve Javadoc for the overloaded method.The overloaded method should have more complete documentation explaining its purpose and relationship to the main method.
/** - * Overloaded method setting the isImportFromSharing flag to false as default + * Imports a programming exercise from an uploaded zip file with default settings. + * This is a convenience method that calls the main import method with isImportFromSharing set to false. + * + * @param programmingExerciseForImport the programming exercise that should be imported + * @param zipFile the zip file that contains the exercise + * @param course the course to which the exercise should be added + * @param user the user initiating the import + * @return the imported programming exercise + * @throws IOException if there is an error reading the files + * @throws GitAPIException if there is an error with Git operations + * @throws URISyntaxException if there is an error with URI parsing */src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java (3)
20-22
: Complete the class-level Javadoc documentation.The class documentation is incomplete and doesn't properly describe the class's purpose and functionality.
/** - * provides infrastructure to set up and shutdown + * Test utility class that provides infrastructure to mock the sharing platform + * for testing purposes. This mock provider simulates connections between Artemis + * and the sharing platform, allowing for isolated testing of sharing functionality. */
76-79
: Complete the method documentation.The Javadoc comment for the
mockStatus
method is incomplete and unclear./** - * registers or shuts down the required + * Mocks the sharing platform connection status. + * When success is true, establishes a connection to simulate a successful platform state. + * When success is false, resets the connection to simulate platform unavailability. * - * @param success Successful response or timeout. + * @param success true to simulate successful connection, false to simulate failure/timeout + * @throws Exception if the mock operation fails */
27-31
: Consider making constants more configurable.The hardcoded URLs and test installation name could be made configurable through properties to improve test flexibility.
-protected static final String TEST_INSTALLATION_NAME = "ArtemisTestInstance"; +@Value("${artemis.sharing.test.installation-name:ArtemisTestInstance}") +private String testInstallationName; -public static final String SHARING_BASEURL = "http://localhost:9001/api"; +@Value("${artemis.sharing.test.base-url:http://localhost:9001/api}") +private String sharingBaseUrl; -public static final String SHARING_BASEURL_PLUGIN = SHARING_BASEURL + "/pluginIF/v0.1"; +private String getSharingPluginUrl() { + return sharingBaseUrl + "/pluginIF/v0.1"; +}src/main/webapp/app/sharing/sharing.scss (2)
16-17
: Define magic numbers as CSS custom properties.The stylesheet contains many magic numbers that should be defined as CSS variables for better maintainability.
Add CSS custom properties at the top of the file:
:host { --curve-height: 100px; --shadow-offset: 1px; --shadow-blur: 40px; --font-size-large: 5em; --font-size-medium: 3em; --font-size-small: 2em; --spacing-small: 20px; --spacing-medium: 35px; --border-radius: 10px; --box-height: 250px; --icon-size-large: 80px; --icon-size-small: 40px; }Then replace magic numbers:
.curve { fill: #fff; - height: 100px; + height: var(--curve-height); width: 100%; position: absolute; bottom: 0; left: 0; }Also applies to: 28-28, 66-66
210-214
: Consolidate repetitive hover animations.The hover animations for feature descriptions are duplicated and could be consolidated using a mixin or shared class.
Create a SCSS mixin for the slide-up animation:
@mixin slide-up-animation($top-position: 100px, $opacity: 1) { transition: 0.5s; &:hover { top: $top-position; opacity: $opacity; text-align: center; } } .container .box .feature-short-description { position: absolute; top: 100%; height: calc(100% - 120px); width: calc(100% - 40px); box-sizing: border-box; font-size: 18px; opacity: 0; @include slide-up-animation(100px, 1); }src/main/webapp/app/sharing/sharing.component.spec.ts (1)
24-203
: Use single quotes consistently throughout the file.The TypeScript style guide requires single quotes for strings, but the file uses a mix of single and double quotes.
Examples of lines that need to be updated:
- Line 72:
{ id: 1, title: 'testCouse 1' },
→{ id: 1, title: 'testCouse 1' },
- Line 73:
{ id: 2, title: 'testCouse 2' },
→{ id: 2, title: 'testCouse 2' },
- Line 88:
courseReq.flush(courses);
uses double quotes in the expectOne- Line 161:
{ message: 'Not Found' }
→{ message: 'Not Found' }
Apply this pattern throughout the file for consistency.
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (3)
139-262
: Use consistent and descriptive test method naming.Test methods have inconsistent naming - some start with "test" prefix while others don't. Follow a consistent naming pattern that describes what is being tested and the expected outcome.
Examples of improved naming:
- void isSharingPlatformUp() + void shouldReturnTrueWhenSharingPlatformIsEnabled() - void testImportBasket() + void shouldSuccessfullyImportBasketFromSharingPlatform() - void testImportBasketFail() + void shouldReturnNotFoundWhenBasketImportFails() - void importBasketNotFound() + void shouldReturnNotFoundWhenBasketDoesNotExist() - void importBasketWrongChecksum() + void shouldReturnBadRequestWhenChecksumIsInvalid()
141-178
: Remove commented out debug statements.Remove the commented
.andDo(print())
statements throughout the test file. These appear to be temporary debug statements that should not be committed.Lines to clean up:
- Line 141:
// .andDo(print())
- Line 177:
/* .andDo(print()) */
- Line 195:
/* .andDo(print()) */
- Line 309:
/* .andDo(print()) */
- Line 350:
.andDo(print())
(this one is not commented and should either be removed or justified)Also applies to: 192-196, 309-310, 349-350, 402-402
359-390
: Consider using DTOs instead of modifying entities for serialization.The
makeCourseJSONSerializable
method is a workaround that modifies entity state to make it serializable. This violates the principle of using DTOs for data transfer and indicates potential architectural issues with directly serializing entities.Consider:
- Creating proper DTOs for data transfer instead of using entities directly
- Using
@JsonIgnore
or@JsonView
annotations to control serialization- Configuring Jackson to handle circular references properly
This would eliminate the need for this workaround method and improve the separation of concerns between persistence and presentation layers.
src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java (3)
50-50
: Remove unnecessary @SuppressWarnings annotation.The
@SuppressWarnings("unused")
annotation on the constructor is unnecessary since this is a standard dependency injection constructor that will be used by Spring.- @SuppressWarnings("unused") public SharingSupportResource(SharingConnectorService sharingConnectorService) {
73-73
: Use a more descriptive variable name.The variable name
apiBaseUrl1
is not descriptive and suggests this is a quick fix rather than intentional naming.- URL apiBaseUrl1; + URL parsedApiBaseUrl; try { - apiBaseUrl1 = URI.create(apiBaseUrl).toURL(); + parsedApiBaseUrl = URI.create(apiBaseUrl).toURL(); } catch (IllegalArgumentException | MalformedURLException e) { log.error("Bad URL", e); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); } - return ResponseEntity.ok(sharingConnectorService.getPluginConfig(apiBaseUrl1, installationName)); + return ResponseEntity.ok(sharingConnectorService.getPluginConfig(parsedApiBaseUrl, installationName));
69-81
: Consider extracting URL validation logic to improve readability.The URL parsing and validation logic could be extracted to a private method to improve the main method's readability and testability.
+ private Optional<URL> parseAndValidateUrl(String apiBaseUrl) { + try { + return Optional.of(URI.create(apiBaseUrl).toURL()); + } catch (IllegalArgumentException | MalformedURLException e) { + log.error("Bad URL: {}", apiBaseUrl, e); + return Optional.empty(); + } + } @GetMapping(SHARINGCONFIG_RESOURCE_PATH) public ResponseEntity<SharingPluginConfig> getConfig(@RequestHeader("Authorization") Optional<String> sharingApiKey, @RequestParam String apiBaseUrl, @RequestParam Optional<String> installationName) { if (sharingApiKey.isPresent() && sharingConnectorService.validate(sharingApiKey.get())) { log.info("Delivered Sharing Config "); - URL apiBaseUrl1; - try { - apiBaseUrl1 = URI.create(apiBaseUrl).toURL(); - } - catch (IllegalArgumentException | MalformedURLException e) { - log.error("Bad URL", e); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); - } - return ResponseEntity.ok(sharingConnectorService.getPluginConfig(apiBaseUrl1, installationName)); + Optional<URL> parsedUrl = parseAndValidateUrl(apiBaseUrl); + if (parsedUrl.isEmpty()) { + return ResponseEntity.badRequest().build(); + } + return ResponseEntity.ok(sharingConnectorService.getPluginConfig(parsedUrl.get(), installationName)); }src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java (4)
16-16
: Use more descriptive test method name.The method name
someEqualsTests
is not descriptive enough. Test method names should clearly indicate what behavior is being tested.- void someEqualsTests() { + void testSharingInfoDTOEquals_shouldRespectAllFieldsExceptChecksum() {
20-22
: Use more specific assertions for equals() contract.The equals() method test should be more comprehensive and use more specific assertions to clearly document the expected behavior.
- assertThat(si.equals(si2)).isTrue(); - assertThat(si.equals(si)).isTrue(); - assertThat(si.equals(null)).isFalse(); + assertThat(si).isEqualTo(si2); + assertThat(si).isEqualTo(si); + assertThat(si).isNotEqualTo(null); + assertThat(si).isNotEqualTo("different type");
70-72
: Split long lines for better readability.The test lines are too long and should be split for better readability according to coding standards.
assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> new SharingMultipartZipFile(null, this.getClass().getResource("./basket/sampleExercise.zip").openStream())); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new SharingMultipartZipFile("no stream", null)); + .isThrownBy(() -> new SharingMultipartZipFile(null, + this.getClass().getResource("./basket/sampleExercise.zip").openStream())); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> new SharingMultipartZipFile("no stream", null));
15-50
: Consider using parameterized tests for equals() method testing.The equals() method testing could be more maintainable and comprehensive using JUnit 5's parameterized tests.
This would improve test maintainability and make it easier to add new test cases:
@ParameterizedTest @MethodSource("provideEqualsTestCases") void testSharingInfoDTOEquals(SharingInfoDTO dto1, SharingInfoDTO dto2, boolean expected) { if (expected) { assertThat(dto1).isEqualTo(dto2); } else { assertThat(dto1).isNotEqualTo(dto2); } } private static Stream<Arguments> provideEqualsTestCases() { SharingInfoDTO base = new SharingInfoDTO(); SharingInfoDTO withPosition = new SharingInfoDTO(); withPosition.setExercisePosition(2); // ... more test cases return Stream.of( Arguments.of(base, new SharingInfoDTO(), true), Arguments.of(base, withPosition, false), // ... more test cases ); }src/main/webapp/app/programming/manage/grading/charts/programming-exercise-detail.component.with-sharing.spec.ts (1)
34-36
: Consider consolidating test files if this becomes stable.The comment suggests this could be merged into the main test file once stabilized. This approach helps avoid test fragmentation.
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java (1)
49-67
: Note limitation with available() method usage.The
isEmpty()
andgetSize()
methods rely onInputStream.available()
, which only returns immediately available bytes and may not accurately reflect the total stream size or emptiness. However, based on previous feedback indicating this is a dummy implementation, this limitation may be acceptable for the current use case.src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ProgrammingExerciseImportFromSharingService.java (1)
65-86
: Consider more specific exception handling.The method has good input validation and delegation patterns, but the catch-all exception block (lines 82-85) that just logs and re-throws may not add significant value. Consider handling specific exceptions or removing the try-catch if it's only for logging.
- try { - return this.programmingExerciseImportFromFileService.importProgrammingExerciseFromFile(sharingSetupInfo.exercise(), zipFileO.get(), sharingSetupInfo.course(), user, - true); - } - catch (Exception e) { - log.error("Cannot create exercise", e); - throw e; - } + return this.programmingExerciseImportFromFileService.importProgrammingExerciseFromFile(sharingSetupInfo.exercise(), zipFileO.get(), sharingSetupInfo.course(), user, + true);src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java (1)
92-92
: Consider moving ObjectMapper to setup method.The ObjectMapper field could be initialized in a @beforeeach method for better organization, or made static if it's immutable and reusable across test instances.
- private final com.fasterxml.jackson.databind.ObjectMapper objectMapper = new ObjectMapper().registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); + private ObjectMapper objectMapper; + + @BeforeEach + void setupObjectMapper() { + objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); + }src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java (4)
147-150
: Improve error message for generic REST exceptions.The error message "Unrecognized property when importing exercise from Sharing" is misleading as it implies a deserialization issue, but this catch block handles all
RestClientException
types.- catch (RestClientException rpe) { - log.warn("Unrecognized property when importing exercise from Sharing", rpe); + catch (RestClientException rpe) { + log.warn("Failed to retrieve basket from sharing platform", rpe); return Optional.empty(); }
227-239
: Consider a more robust solution for Docker URL handling.The current implementation is described as "just a weak implementation for local testing". For production use, consider using environment-specific configuration instead of string replacement.
Consider using Spring profiles or environment variables to configure the correct host URL for different environments (local, Docker, production) rather than runtime string replacement.
278-281
: Remove redundant empty check.The empty check is immediately followed by throwing an exception, making the if statement unnecessary.
Optional<String> entryFromBasket = this.getEntryFromBasket(pattern, sharingInfo); - if (entryFromBasket.isEmpty()) - throw new NotFoundException("Could not retrieve exercise details from imported exercise"); - - String exerciseDetailString = entryFromBasket.get(); + String exerciseDetailString = entryFromBasket.orElseThrow( + () -> new NotFoundException("Could not retrieve exercise details from imported exercise") + );
322-322
: Extract buffer size as a constant.The hardcoded buffer size should be defined as a class constant for better maintainability.
Add a constant at the class level:
private static final int BUFFER_SIZE = 102400;Then use it:
- byte[] buffer = new byte[102400]; + byte[] buffer = new byte[BUFFER_SIZE];src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java (4)
109-117
: Fix duplicate field documentation.Both fields have the same comment "the shared secret api key", which is confusing.
/** - * the shared secret api key + * the action name for the sharing platform */ @Value("${artemis.sharing.actionname:Export to Artemis@somewhere}") private String actionName;
199-204
: Improve installation name resolution for containerized environments.
InetAddress.getLocalHost()
may return unhelpful values like "localhost" or container IDs in containerized environments.try { - this.installationName = installationName.orElse(InetAddress.getLocalHost().getCanonicalHostName()); + this.installationName = installationName + .or(() -> Optional.ofNullable(System.getenv("HOSTNAME"))) + .orElse(InetAddress.getLocalHost().getCanonicalHostName()); } catch (UnknownHostException e) { + log.warn("Failed to determine hostname", e); this.installationName = UNKNOWN_INSTALLATIONAME; }
219-224
: Document or make configurable the API key length limit.The hardcoded length limit of 200 is arbitrary and undocumented.
+ private static final int MAX_API_KEY_LENGTH = 200; + public boolean validate(String apiKey) { - if (apiKey == null || apiKey.length() > 200) { + if (apiKey == null || apiKey.length() > MAX_API_KEY_LENGTH) { // this is just in case, somebody tries an attack lastHealthStati.add(new HealthStatus("Failed api Key validation"));
275-278
: Log the full exception for better debugging.Only logging the exception message loses valuable stack trace information.
catch (Exception e) { - log.info("Failed to request reinitialization from Sharing Platform: {}", e.getMessage()); + log.warn("Failed to request reinitialization from Sharing Platform", e); }src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts (3)
82-96
: Consider using a facade pattern to reduce component dependencies.The component has 14 service dependencies, which suggests it might be handling too many responsibilities. Consider creating a facade service to encapsulate related operations.
Group related services (e.g., all exercise-related services) behind a facade to simplify the component and improve testability.
132-135
: Integrate sharing field visibility logic into the general approach.The special case for
SHORT_NAME
field whensharingInfo
is present seems like a workaround that breaks the component's field visibility pattern.Consider adding sharing import as a proper mode in the
IS_DISPLAYED_IN_SIMPLE_MODE
mapping or creating a more flexible field visibility system that can handle different import types consistently.
352-353
: Consolidate import type checks to reduce duplication.The repeated checks for
isImportFromFile || isImportFromSharing
violate the DRY principle.Add a computed property or getter:
get isImportFromExternal(): boolean { return this.isImportFromFile || this.isImportFromSharing; }Then replace all occurrences of
this.isImportFromFile || this.isImportFromSharing
withthis.isImportFromExternal
.Also applies to: 373-374, 520-520
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java (1)
53-71
: Remove redundant JavaDoc comments for fields.The JavaDoc comments for the fields simply restate the field names without adding value. Consider removing them to reduce noise in the code.
- /** - * a logger - */ private final Logger log = LoggerFactory.getLogger(ExerciseSharingResource.class); - /** - * the exercise sharing service - */ private final ExerciseSharingService exerciseSharingService; - /** - * the sharing connector service - */ private final SharingConnectorService sharingConnectorService; - /** - * the programming-exercise import from Sharing Service - */ private final ProgrammingExerciseImportFromSharingService programmingExerciseImportFromSharingService;src/main/webapp/app/sharing/sharing.route.ts (1)
16-23
: Simplify the route structure.The current implementation creates unnecessary complexity with
SHARING_ROUTES
constant and nested structure for a single route. Consider simplifying the export.-const SHARING_ROUTES = [...sharingRoutes]; - export const featureOverviewState: Routes = [ { path: '', - children: SHARING_ROUTES, + children: sharingRoutes, }, ];This reduces the intermediate constant while maintaining the same functionality.
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java (2)
18-22
: Consider using static mocks instead of dependency injection.The test uses
@Autowired
forSharingPlatformMockProvider
andSharingConnectorService
, but the coding guidelines recommend "mock_strategy: static_mocks". Consider using@MockBean
or static mocking frameworks like Mockito's static mocking capabilities.
57-57
: Optimize the huge key generation for better performance.The current approach creates a very large string using
repeat(50)
which may be inefficient. Consider using a more concise approach for generating an oversized key.-String hugeKey = "huge" + "0123456789".repeat(50); +String hugeKey = "x".repeat(1000); // More concise way to create a large key
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (16)
.github/workflows/build.yml
is excluded by!**/*.yml
docs/admin/setup/sharing/sharingButtonArtemis.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharingButtonSharing.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharing_health1.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharing_health2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/artemis_import.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/artemis_import2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_export.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_metadata.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_metadata2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_namespace.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_search.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_success.png
is excluded by!**/*.png
,!**/*.png
src/main/resources/config/application-dev.yml
is excluded by!**/*.yml
src/main/resources/config/application-sharing.yml
is excluded by!**/*.yml
src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleExercise.zip
is excluded by!**/*.zip
,!**/*.zip
📒 Files selected for processing (54)
build.gradle
(2 hunks)docker/artemis/config/dev-local-vc-local-ci.env
(1 hunks)docs/admin/extension-services.rst
(1 hunks)docs/admin/setup/sharing.rst
(1 hunks)docs/index.rst
(1 hunks)docs/user/sharing.rst
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/Constants.java
(3 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/RestTemplateConfiguration.java
(2 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/dto/SharingInfoDTO.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
(4 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ProgrammingExerciseImportFromSharingService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
(1 hunks)src/main/webapp/app/app.routes.ts
(1 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.html
(1 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts
(7 hunks)src/main/webapp/app/programming/manage/exercise/programming-exercise.component.ts
(1 hunks)src/main/webapp/app/programming/manage/grading/charts/programming-exercise-detail.component.with-sharing.spec.ts
(1 hunks)src/main/webapp/app/programming/manage/programming-exercise-management.route.ts
(1 hunks)src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
(1 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-creation-config.ts
(1 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-update.component.html
(3 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
(17 hunks)src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/search-result-dto.model.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.html
(1 hunks)src/main/webapp/app/sharing/sharing.component.spec.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/sharing.model.ts
(1 hunks)src/main/webapp/app/sharing/sharing.route.ts
(1 hunks)src/main/webapp/app/sharing/sharing.scss
(1 hunks)src/main/webapp/i18n/de/programmingExercise.json
(1 hunks)src/main/webapp/i18n/de/sharing.json
(1 hunks)src/main/webapp/i18n/en/programmingExercise.json
(1 hunks)src/main/webapp/i18n/en/sharing.json
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationIndependentTest.java
(2 hunks)src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java
(3 hunks)src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleBasket.json
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
`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:PascalC...
src/main/webapp/app/programming/manage/exercise/programming-exercise.component.ts
src/main/webapp/app/app.routes.ts
src/main/webapp/app/programming/manage/programming-exercise-management.route.ts
src/main/webapp/app/sharing/sharing.route.ts
src/main/webapp/app/sharing/sharing.model.ts
src/main/webapp/app/sharing/search-result-dto.model.ts
src/main/webapp/app/programming/manage/update/programming-exercise-creation-config.ts
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts
src/main/webapp/app/sharing/sharing.component.ts
src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
src/main/webapp/app/sharing/sharing.component.spec.ts
src/main/webapp/app/programming/manage/grading/charts/programming-exercise-detail.component.with-sharing.spec.ts
src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
`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/**/*.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/programming/manage/detail/programming-exercise-detail.component.html
src/main/webapp/app/sharing/sharing.component.html
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.html
`src/main/webapp/i18n/de/**/*.json`: German language translations should be informal (dutzen) and should never be formal (sietzen). So the user should always be addressed with "du/...
src/main/webapp/i18n/de/**/*.json
: German language translations should be informal (dutzen) and should never be formal (sietzen). So the user should always be addressed with "du/dein" and never with "sie/ihr".
src/main/webapp/i18n/de/programmingExercise.json
src/main/webapp/i18n/de/sharing.json
`src/main/java/**/*.java`: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,de...
src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java
src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
src/main/java/de/tum/cit/aet/artemis/core/config/RestTemplateConfiguration.java
src/main/java/de/tum/cit/aet/artemis/core/dto/SharingInfoDTO.java
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java
src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java
src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java
src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ProgrammingExerciseImportFromSharingService.java
src/main/java/de/tum/cit/aet/artemis/core/config/Constants.java
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...
src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java
src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationIndependentTest.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingServiceTest.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java
🧠 Learnings (2)
src/main/webapp/app/sharing/search-result-dto.model.ts (3)
Learnt from: Wallenstein61
PR: ls1intum/Artemis#9909
File: src/main/webapp/app/sharing/search-result-dto.model.ts:17-42
Timestamp: 2025-02-11T15:46:08.133Z
Learning: The types in `UserProvidedMetadataDTO` interface in `search-result-dto.model.ts` must match the sharing platform's contract exactly to maintain compatibility. Avoid modifying these types even if they don't follow internal TypeScript conventions.
Learnt from: Wallenstein61
PR: ls1intum/Artemis#9909
File: src/main/webapp/app/sharing/search-result-dto.model.ts:57-65
Timestamp: 2025-02-11T15:40:53.440Z
Learning: The ProjectDTO interface in src/main/webapp/app/sharing/search-result-dto.model.ts uses snake_case property names to maintain compatibility with the external sharing platform's API contract, which is an intentional deviation from Angular's camelCase convention.
Learnt from: Wallenstein61
PR: ls1intum/Artemis#9909
File: src/main/webapp/app/sharing/search-result-dto.model.ts:50-55
Timestamp: 2025-02-11T15:46:35.616Z
Learning: The `IExerciseType` enum in `src/main/webapp/app/sharing/search-result-dto.model.ts` must maintain its current naming (with 'I' prefix) and lowercase string values to ensure compatibility with the external sharing platform connector interface. This is an intentional exception to our TypeScript naming conventions.
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java (1)
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10593
File: src/main/java/de/tum/cit/aet/artemis/sharing/SharingMultipartZipFile.java:60-68
Timestamp: 2025-04-11T07:12:15.912Z
Learning: The SharingMultipartZipFile class in the sharing package is a dummy implementation that's not actually used in practice, so optimizations aren't necessary.
🧬 Code Graph Analysis (4)
src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java (2)
src/main/webapp/app/core/layouts/profiles/shared/profile.service.ts (1)
isProfileActive
(30-32)src/main/java/de/tum/cit/aet/artemis/core/config/Constants.java (1)
Constants
(8-523)
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts (1)
src/main/webapp/app/shared/detail-overview-list/detail-overview-list.component.ts (1)
DetailOverviewSection
(26-29)
src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts (2)
src/main/webapp/app/sharing/sharing.model.ts (2)
Injectable
(3-45)ShoppingBasket
(50-54)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)
src/main/webapp/app/sharing/sharing.component.spec.ts (4)
src/test/javascript/spec/helpers/mocks/service/mock-alert.service.ts (1)
MockAlertService
(3-8)src/test/javascript/spec/helpers/mocks/mock-router.ts (1)
MockRouter
(5-30)src/main/webapp/app/sharing/sharing.model.ts (1)
ShoppingBasket
(50-54)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)
🪛 GitHub Check: client-tests
src/main/webapp/app/programming/manage/grading/charts/programming-exercise-detail.component.with-sharing.spec.ts
[failure] 20-20:
Cannot find module '../../helpers/mocks/service/mock-sync-storage.service' or its corresponding type declarations.
[failure] 18-18:
Cannot find module '../../helpers/mocks/service/mock-ngb-modal.service' or its corresponding type declarations.
[failure] 14-14:
Cannot find module '../../helpers/mocks/service/mock-programming-exercise.service' or its corresponding type declarations.
[failure] 12-12:
Cannot find module '../../helpers/mocks/service/mock-profile.service' or its corresponding type declarations.
[failure] 11-11:
Cannot find module 'app/shared/layouts/profiles/profile.service' or its corresponding type declarations.
[failure] 9-9:
Cannot find module 'app/shared/statistics-graph/statistics.service' or its corresponding type declarations.
[failure] 7-7:
Cannot find module 'app/entities/course.model' or its corresponding type declarations.
[failure] 6-6:
Cannot find module '../../helpers/mocks/activated-route/mock-activated-route' or its corresponding type declarations.
[failure] 5-5:
Cannot find module 'app/entities/programming/programming-exercise.model' or its corresponding type declarations.
[failure] 4-4:
Cannot find module 'app/programming/manage/programming-exercise-detail.component' or its corresponding type declarations.
🪛 GitHub Check: client-tests-selected
src/main/webapp/app/programming/manage/grading/charts/programming-exercise-detail.component.with-sharing.spec.ts
[failure] 20-20:
Cannot find module '../../helpers/mocks/service/mock-sync-storage.service' or its corresponding type declarations.
[failure] 18-18:
Cannot find module '../../helpers/mocks/service/mock-ngb-modal.service' or its corresponding type declarations.
[failure] 14-14:
Cannot find module '../../helpers/mocks/service/mock-programming-exercise.service' or its corresponding type declarations.
[failure] 12-12:
Cannot find module '../../helpers/mocks/service/mock-profile.service' or its corresponding type declarations.
[failure] 11-11:
Cannot find module 'app/shared/layouts/profiles/profile.service' or its corresponding type declarations.
[failure] 9-9:
Cannot find module 'app/shared/statistics-graph/statistics.service' or its corresponding type declarations.
[failure] 7-7:
Cannot find module 'app/entities/course.model' or its corresponding type declarations.
[failure] 6-6:
Cannot find module '../../helpers/mocks/activated-route/mock-activated-route' or its corresponding type declarations.
[failure] 5-5:
Cannot find module 'app/entities/programming/programming-exercise.model' or its corresponding type declarations.
[failure] 4-4:
Cannot find module 'app/programming/manage/programming-exercise-detail.component' or its corresponding type declarations.
🪛 ast-grep (0.38.1)
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
[warning] 404-404: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Mac.getInstance(algorithm)
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html
(use-of-aes-ecb-java)
[warning] 404-404: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Mac.getInstance(algorithm)
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA
(desede-is-deprecated-java)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Build .war artifact
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Analyse
- GitHub Check: client-style
- GitHub Check: server-style
- GitHub Check: server-tests
🔇 Additional comments (70)
src/main/webapp/app/programming/manage/exercise/programming-exercise.component.ts (2)
57-57
: VerifySharingInfo
is injectable
Ensure the importedSharingInfo
class fromapp/sharing/sharing.model
is decorated with@Injectable()
; otherwise providing it here will cause a runtime injection error.
61-61
: Confirm provider scope
AddingSharingInfo
toproviders
gives each component instance its own copy. If you need a singleton across the app, prefer usingprovidedIn: 'root'
in the service definition instead of per-component providers.docs/index.rst (1)
42-42
: Good integration in User Guide toctree
The newuser/sharing
entry correctly surfaces the sharing platform guide alongside other user docs.docs/admin/extension-services.rst (1)
36-36
: Extension Services toctree updated
Includingsetup/sharing
here properly advertises the sharing extension in the administration docs.docker/artemis/config/dev-local-vc-local-ci.env (1)
5-5
: Activate sharing profile in Docker config
Appendingsharing
toSPRING_PROFILES_ACTIVE
correctly enables the new Spring profile in the local CI/VC Docker environment.src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.html (1)
95-97
: Verify conditional rendering
Confirm thatisExportToSharingEnabled
is declared and updated in the component class (via theProgrammingExerciseSharingService
) so the sharing button toggles as expected.src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationIndependentTest.java (2)
11-11
: Add PROFILE_SHARING import for shared tests
The new sharing profile constant is correctly imported alongside existing profiles to enable sharing-related beans in independent tests.
47-48
: Activate sharing profile in base test
IncludingPROFILE_SHARING
in@ActiveProfiles
ensures independent tests load sharing beans and contexts as intended.src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java (1)
242-243
: Permit all for sharing endpoints
Explicitly allowing unauthenticated access to/api/sharing/**
is acceptable here since controllers enforce security tokens.src/main/webapp/app/programming/manage/update/programming-exercise-creation-config.ts (1)
25-25
: AddisImportFromSharing
flag
IntroducingisImportFromSharing
aligns the creation config with other import flags and supports the sharing workflow.build.gradle (2)
160-162
: Add SharingPluginPlatformAPI dependency
The new dependency onorg.codeability:SharingPluginPlatformAPI:1.1.1
is necessary for sharing platform integration.
455-457
: Apply test.gradle after dependencies block
Moving theapply from: "gradle/test.gradle"
ensures themockitoAgent
configuration is applied without conflicts.src/main/webapp/i18n/de/sharing.json (1)
1-22
: LGTM! German localization follows informal language guidelines.The German translations correctly use informal language (dutzen) as required by the coding guidelines:
- "Bitte melden dich als Instruktor an!" uses "dich" (informal)
- Other strings are appropriately informal and professional
The JSON structure is well-organized and follows established i18n patterns.
src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java (3)
13-13
: LGTM! Proper import of sharing profile constant.The import follows the established pattern for other profile constants and is necessary for the sharing feature integration.
78-78
: LGTM! Sharing profile properly added to test configuration.The
PROFILE_SHARING
addition to active profiles ensures that sharing-related functionality is available during integration tests, which aligns with the comprehensive testing approach mentioned in the PR objectives.
136-140
: LGTM! Well-documented DockerClient field for test infrastructure.The protected
DockerClient
field with clear documentation allows subclasses to dynamically mock Docker operations, which supports the testing requirements for the local CI functionality mentioned in the PR.src/main/webapp/i18n/en/programmingExercise.json (1)
745-751
: LGTM! Well-structured English localization for sharing functionality.The sharing localization strings are:
- Clearly written and professional
- Properly structured within the artemisIntelligence section
- Include appropriate error handling with dynamic message placeholder
- Consistent with existing i18n patterns in the file
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.html (1)
3-3
: LGTM! Consistent integration of sharing import support.The addition of
isImportFromSharing
flag is implemented consistently across all relevant conditions in the template. The changes properly extend the existing import logic to support the new sharing platform import source while maintaining the correct conditional display behavior.Also applies to: 7-7, 19-19, 65-65
src/main/webapp/i18n/de/programmingExercise.json (1)
745-752
: LGTM! Proper German localization for sharing feature.The new sharing-related translations are well-structured and use appropriate German terminology. The translations don't directly address the user, so the informal/formal language guideline doesn't apply here. The error message template correctly uses interpolation syntax.
src/main/java/de/tum/cit/aet/artemis/core/config/RestTemplateConfiguration.java (2)
8-8
: LGTM! Proper import addition.The import for
PROFILE_SHARING
follows the existing pattern and is correctly placed with other profile imports.
63-74
: LGTM! Well-implemented RestTemplate bean.The
sharingRestTemplate()
method follows the established pattern in this configuration class and adheres to Java coding guidelines:
- Proper JavaDoc documentation explaining the purpose
- Correct use of
@Bean
and@Profile
annotations- Code reuse by calling existing
createRestTemplate()
method- Consistent with other RestTemplate bean definitions
src/main/webapp/app/programming/manage/programming-exercise-management.route.ts (1)
59-70
: LGTM! Consistent route configuration for sharing import.The new route follows the established pattern in this file and adheres to Angular coding guidelines:
- Uses lazy loading with proper component import
- Correctly applies the existing resolver, authorities, and guards
- Consistent structure with other import routes
- Proper page title configuration
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java (2)
12-14
: LGTM! Well-structured record implementation.The record follows Java naming conventions and coding guidelines perfectly. The @Profile annotation ensures proper activation scope, and the record serves as an appropriate DTO combining related data.
12-13
: LGTM! Well-designed record for DTO pattern.This record effectively follows the coding guidelines for DTOs using Java records with minimal data and single responsibility. The profile annotation correctly gates the sharing feature.
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java (4)
13-32
: LGTM! Excellent test class structure.The test class follows all coding guidelines perfectly:
- Extends appropriate base test class
- Uses constructor injection via @Autowired
- Implements proper setup/teardown with mock provider
- Uses descriptive test naming conventions
34-45
: Good configuration properties testing.The test properly verifies configuration properties and API key behavior using the recommended assertThat assertions.
47-73
: Comprehensive API key validation test coverage.Excellent test coverage for various API key scenarios:
- Null keys
- Invalid large keys
- Valid keys (with and without Bearer prefix)
- Fake/invalid keys
All tests use proper assertThat assertions as required by coding guidelines.
47-74
: Excellent API key validation test coverage.The API key validation tests cover all edge cases appropriately with descriptive names and specific assertions. The test scenarios (null, oversized, valid, and fake keys) provide comprehensive coverage.
src/main/webapp/app/sharing/sharing.route.ts (5)
1-4
: LGTM! Proper imports and conventions.The imports follow Angular conventions and use the required single quotes for strings. Good use of authority constants for role-based access control.
5-14
: Well-structured route configuration.The route configuration follows Angular routing best practices:
- Proper authority restrictions for EDITOR, INSTRUCTOR, and ADMIN roles
- Correct page title configuration for localization
- Clean route structure
16-23
: Good routing architecture pattern.The use of spread operator and nested route structure provides flexibility for future route additions while maintaining clean organization.
5-14
: Well-structured route configuration.The route configuration follows Angular style guidelines correctly with proper authority restrictions, localization key usage, and single quotes for strings.
1-23
: LGTM! Clean routing implementation.The routing configuration follows Angular best practices and coding guidelines effectively:
- Uses single quotes for strings ✓
- Proper authority restrictions for EDITOR, INSTRUCTOR, and ADMIN ✓
- Clean route structure with appropriate nesting ✓
- Follows Angular style guide conventions ✓
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java (4)
10-15
: LGTM! Proper exception class structure.Good use of @Profile annotation and proper serialVersionUID declaration following Java serialization best practices.
32-42
: Good static factory method pattern.The
connectionError
static factory method is well-designed and properly documented. It provides a convenient way to create connection-specific exceptions with a consistent message format.
10-14
: Well-designed exception class structure.The exception class properly extends Exception, uses the sharing profile, and includes the required serialVersionUID for serialization compatibility.
39-41
: LGTM! Well-designed static factory method.The
connectionError
static factory method provides a clear and convenient way to create connection-specific exceptions with proper formatting.src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingServiceTest.java (1)
17-17
: Approve the test class structure.The test class properly extends the appropriate base test class and follows the established testing patterns with proper setup and teardown methods.
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java (1)
58-67
: 🛠️ Refactor suggestionImprove error handling and method documentation.
The method should handle potential JSON parsing errors more gracefully and validate the response content.
public SharingPluginConfig connectRequestFromSharingPlatform() throws Exception { + if (sharingApiKey == null || sharingApiKey.isBlank()) { + throw new IllegalStateException("Sharing API key is not configured for testing"); + } + MvcResult result = restMockMvc - .perform(get("/api/sharing/config").queryParam("apiBaseUrl", SHARING_BASEURL_PLUGIN).queryParam("installationName", TEST_INSTALLATION_NAME) + .perform(get("/api/sharing/config").queryParam("apiBaseUrl", getSharingPluginUrl()).queryParam("installationName", testInstallationName) .header("Authorization", sharingApiKey).contentType(MediaType.APPLICATION_JSON)) .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpected(status().isOk()).andReturn(); String content = result.getResponse().getContentAsString(); + + if (content == null || content.isBlank()) { + throw new IllegalStateException("Empty response received from sharing config endpoint"); + } + SharingPluginConfig sharingPluginConfig = objectMapper.readerFor(SharingPluginConfig.class).readValue(content); assertThat(sharingPluginConfig.pluginName).isEqualTo("Artemis Sharing Connector"); return sharingPluginConfig; }Likely an incorrect or invalid review comment.
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts (4)
55-55
: LGTM! Clean integration of sharing functionality.The imports and component registration follow Angular best practices. The sharing component is properly added to the imports array for standalone component usage.
Also applies to: 69-70, 92-92
159-159
: Good implementation of feature flag pattern.The
isExportToSharingEnabled
flag and subscription management follow Angular patterns correctly. Constructor injection of the sharing service is appropriate.Also applies to: 173-173, 179-179
275-286
: Well-implemented subscription with proper error handling.The subscription to
isSharingEnabled()
includes appropriate error handling that defaults tofalse
when the sharing service is unavailable. The mapping and error handling are robust.
294-294
: Memory leak prevention properly implemented.The sharing subscription is correctly unsubscribed in
ngOnDestroy()
, preventing memory leaks.src/main/java/de/tum/cit/aet/artemis/core/config/Constants.java (2)
369-372
: Well-documented profile constant.The
PROFILE_SHARING
constant follows Java naming conventions and includes clear documentation.
375-377
: Consistent API path constants.The sharing-related path constants are well-organized and follow the existing pattern. The
SHARINGCONFIG_RESOURCE_IS_ENABLED
constant correctly builds upon the base path.Also applies to: 484-497
src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts (5)
20-27
: Service structure follows Angular best practices.The service uses proper dependency injection and follows the singleton pattern with
providedIn: 'root'
. The API endpoint definitions are appropriately organized.
32-39
: HTTP request properly structured.The
getSharedExercises
method correctly uses observables and HTTP client patterns. The parameter passing and response mapping are well-implemented.
72-93
: Excellent circular reference handling.The
convertDataFromClient
method properly handles circular dependencies by removing the exercise reference from participations. This prevents serialization issues when sending data to the backend.
101-110
: Robust date conversion handling.The date conversion method properly handles potential undefined values and uses dayjs consistently with the rest of the application.
128-132
: Simple and effective feature detection.The
isSharingEnabled
method provides a clean way to check if sharing functionality is available. The HTTP response approach allows for proper error handling upstream.src/main/webapp/app/sharing/search-result-dto.model.ts (4)
4-12
: Well-structured main interface.The
SearchResultDTO
interface provides a comprehensive structure for sharing platform search results, including all necessary metadata and statistics.
65-70
: Enum naming follows external API requirements.The
IExerciseType
enum maintains compatibility with the sharing platform connector interface. The naming convention (with 'I' prefix and lowercase values) is intentionally different from internal TypeScript conventions to ensure external API compatibility.
75-83
: Snake_case naming required for API compatibility.The
ProjectDTO
interface correctly uses snake_case property names to maintain compatibility with the external sharing platform's API contract, which is an intentional and necessary deviation from Angular's camelCase convention.
26-51
: Comprehensive metadata interface.The
UserProvidedMetadataDTO
interface correctly matches the sharing platform's contract. The types and structure are essential for maintaining API compatibility.docs/admin/setup/sharing.rst (1)
1-95
: Excellent documentation structure and completeness.This setup guide provides comprehensive coverage of the sharing integration configuration, including:
- Clear prerequisites and access requirements
- Multiple configuration options (YAML and environment variables)
- Instructor access setup with EduID and GitLab options
- Troubleshooting guidance with health indicators
- Visual aids to guide administrators
The documentation follows a logical flow and provides practical examples for both Docker and non-Docker deployments.
docs/user/sharing.rst (1)
1-111
: Well-structured user guide with clear workflows.This user documentation effectively guides users through the sharing functionality with:
- Clear background information and prerequisites
- Step-by-step export process with metadata guidance
- Detailed import workflow from the sharing platform
- Helpful screenshots and visual aids throughout
- Practical tips about login requirements and potential issues
The documentation balances technical accuracy with user-friendliness.
src/main/webapp/app/programming/manage/grading/charts/programming-exercise-detail.component.with-sharing.spec.ts (3)
110-153
: Well-structured test setup with comprehensive mocking.The beforeEach setup properly configures all necessary mocks and dependencies for testing the sharing functionality. The use of MockProvider and specific mock services follows Angular testing best practices.
173-184
: Good test coverage for successful sharing configuration.The test properly verifies that the component sets
isExportToSharingEnabled
to true when the sharing API returns a successful response.
186-203
: Effective error handling test for sharing configuration.The test correctly validates that the component handles 404 responses gracefully by setting
isExportToSharingEnabled
to false.src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java (3)
15-19
: Well-structured class with appropriate profile activation.The class properly implements required interfaces and uses the sharing profile for conditional activation.
25-31
: Excellent defensive programming in constructor.Proper null validation and buffering of the input stream improve reliability and performance.
86-90
: Proper resource cleanup implementation.The close method safely handles null checks and properly closes the underlying stream.
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ProgrammingExerciseImportFromSharingService.java (2)
21-57
: Excellent Spring service structure with proper dependency injection.The service correctly uses constructor injection, profile activation, and has well-defined dependencies with clear responsibilities.
75-77
: Good defensive programming for course assignment.The code properly handles the case where the exercise doesn't have an associated course and assigns it from the sharing setup info.
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java (4)
39-63
: Well-structured integration test setup.The test class properly extends the base integration test class and correctly manages the sharing platform mock with appropriate setup and teardown methods.
77-90
: Effective error scenario testing.The test properly verifies error handling when repositories are missing, checking both status code and error message content.
94-125
: Comprehensive successful export test.The test thoroughly validates the export workflow, including redirect URL parsing, parameter validation, and ZIP file download verification.
127-137
: Well-implemented URL parsing helper method.The helper method properly handles URI parsing and parameter encoding for MockMvc request building. Good separation of concerns.
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java (2)
105-117
: Security properly implemented with PreAuthorize.Good use of PreAuthorize annotation to restrict access to instructors, editors, and admins. The method properly delegates the business logic to the service layer.
119-147
:✅ Verification successful
Verify security configuration for sharing endpoints.
The AI summary mentions that security configuration allows unauthenticated access to
/api/sharing/**
endpoints. Please verify if the missing PreAuthorize annotations ongetProblemStatement
andgetExerciseDetails
are intentional, given that these endpoints handle checksum validation internally.
🏁 Script executed:
#!/bin/bash # Description: Check security configuration for sharing endpoints # Search for security configuration related to sharing endpoints rg -A 10 -B 5 "/api/sharing" --glob "**/*Security*.java" --glob "**/*Config*.java" # Check if there are any security filters or configurations specifically for sharing fd -e java -x grep -l "sharing.*permit" {} \; # Look for WebSecurityConfig or similar files that might configure these endpoints ast-grep --pattern 'class $_ { $$$ "/api/sharing" $$$ }'Length of output: 2716
Security configuration for
/api/sharing/**
is intentional
TheSecurityConfiguration
explicitly calls.requestMatchers("/api/sharing/**").permitAll()
with the note that sharing endpoints enforce their own checksum‐based token validation. The absence of@PreAuthorize
ongetProblemStatement
andgetExerciseDetails
is therefore expected—no changes needed.
src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
Outdated
Show resolved
Hide resolved
@Test | ||
void validationFailure() { | ||
assertThat(exerciseSharingService.validate("invalidToken", "invalid sec")).isFalse(); | ||
assertThat(exerciseSharingService.getExportedExerciseByToken("invalidToken")).isNull(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve test method name and add more comprehensive validation tests.
The test method name validationFailure()
is not very descriptive according to the coding guidelines requiring descriptive test naming. Also, the test could be more comprehensive by testing edge cases and different validation scenarios.
@Test
-void validationFailure() {
+void shouldReturnFalseForInvalidTokenAndSecurityString() {
assertThat(exerciseSharingService.validate("invalidToken", "invalid sec")).isFalse();
assertThat(exerciseSharingService.getExportedExerciseByToken("invalidToken")).isNull();
}
+
+@Test
+void shouldReturnFalseForNullTokenAndSecurityString() {
+ assertThat(exerciseSharingService.validate(null, null)).isFalse();
+ assertThat(exerciseSharingService.getExportedExerciseByToken(null)).isNull();
+}
+
+@Test
+void shouldReturnFalseForEmptyTokenAndSecurityString() {
+ assertThat(exerciseSharingService.validate("", "")).isFalse();
+ assertThat(exerciseSharingService.getExportedExerciseByToken("")).isNull();
+}
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingServiceTest.java
around lines 35 to 39, rename the test method from validationFailure() to a more
descriptive name that clearly indicates it tests invalid token and security
string validation. Additionally, expand the test to cover more validation
scenarios and edge cases, such as null or empty tokens, valid tokens with
invalid security strings, and any other relevant cases to ensure comprehensive
validation coverage.
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
Outdated
Show resolved
Hide resolved
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
Show resolved
Hide resolved
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
Show resolved
Hide resolved
/** | ||
* GET /sharing/export/{exerciseToken}: Endpoint exposing an exported exercise zip to Sharing | ||
* | ||
* @param token in base64 format and used to retrieve the exercise | ||
* @return a stream of the zip file | ||
* @throws FileNotFoundException if zip file does not exist anymore | ||
*/ | ||
@GetMapping(SHARINGEXPORT_RESOURCE_PATH + "/{token}") | ||
// Custom Key validation is applied | ||
public ResponseEntity<Resource> exportExerciseToSharing(@PathVariable("token") String token, @RequestParam("sec") String sec) throws FileNotFoundException { | ||
if (sec.isEmpty() || !exerciseSharingService.validate(token, sec)) { | ||
log.warn("Security Token {} is not valid", sec); | ||
return ResponseEntity.status(401).body(null); | ||
} | ||
File zipFile = exerciseSharingService.getExportedExerciseByToken(token); | ||
|
||
if (zipFile == null) { | ||
return ResponseEntity.notFound().build(); | ||
} | ||
|
||
/* | ||
* Customized FileInputStream to delete and therefore clean up the returned files | ||
*/ | ||
class NewFileInputStream extends FileInputStream { | ||
|
||
final File file; | ||
|
||
public NewFileInputStream(@NotNull File file) throws FileNotFoundException { | ||
super(file); | ||
this.file = file; | ||
} | ||
|
||
public void close() throws IOException { | ||
super.close(); | ||
if (!file.delete()) { | ||
log.warn("Could not delete imported file from Sharing-Platform"); | ||
} | ||
} | ||
} | ||
|
||
InputStreamResource resource = new InputStreamResource(new NewFileInputStream(zipFile)); | ||
|
||
return ResponseEntity.ok().contentLength(zipFile.length()).contentType(MediaType.APPLICATION_OCTET_STREAM).header("filename", zipFile.getName()).body(resource); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Extract inner class and improve file handling.
- The inner class
NewFileInputStream
violates single responsibility principle and should be extracted - File deletion might fail silently and is not OS-independent
- Return proper error response instead of null
Extract the inner class to a separate utility class:
// In a separate file or as a private static class
public class AutoDeletingFileInputStream extends FileInputStream {
private final File file;
public AutoDeletingFileInputStream(@NotNull File file) throws FileNotFoundException {
super(file);
this.file = file;
}
@Override
public void close() throws IOException {
try {
super.close();
} finally {
try {
Files.deleteIfExists(file.toPath());
} catch (IOException e) {
log.warn("Could not delete imported file from Sharing-Platform: {}", e.getMessage());
}
}
}
}
Update the method:
if (sec.isEmpty() || !exerciseSharingService.validate(token, sec)) {
log.warn("Security Token {} is not valid", sec);
- return ResponseEntity.status(401).body(null);
+ return ResponseEntity.status(401).build();
}
And use the extracted class:
- InputStreamResource resource = new InputStreamResource(new NewFileInputStream(zipFile));
+ InputStreamResource resource = new InputStreamResource(new AutoDeletingFileInputStream(zipFile));
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
around lines 171 to 214, extract the inner class NewFileInputStream into a
separate utility class named AutoDeletingFileInputStream to adhere to single
responsibility principle. Replace the file deletion logic with
Files.deleteIfExists(file.toPath()) inside a finally block in the close method
to handle OS-independent deletion and catch any IOException to log a warning
with the exception message. Also, update the exportExerciseToSharing method to
use this new class and return a proper error response instead of null when the
security token is invalid.
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review continued from previous batch...
/** | ||
* GET .../sharing-import/basket | ||
* | ||
* @return the ResponseEntity with status 200 (OK) and with body the Shopping Basket, or with status 404 (Not Found) | ||
*/ | ||
@GetMapping("/sharing/import/basket") | ||
public ResponseEntity<ShoppingBasket> loadShoppingBasket(@RequestParam String basketToken, @RequestParam String returnURL, @RequestParam String apiBaseURL, | ||
@RequestParam String checksum) { | ||
if (SecretChecksumCalculator.checkChecksum(Map.of("returnURL", returnURL, "apiBaseURL", apiBaseURL), sharingConnectorService.getSharingApiKeyOrNull(), checksum)) { | ||
Optional<ShoppingBasket> sharingInfoDTO = exerciseSharingService.getBasketInfo(basketToken, apiBaseURL); | ||
return ResponseUtil.wrapOrNotFound(sharingInfoDTO); | ||
} | ||
else { | ||
log.warn("Checksum validation failed for basketToken={}", basketToken); | ||
return ResponseEntity.badRequest().body(null); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Return proper error response instead of null body.
When checksum validation fails, the method returns a null body which is not RESTful. Consider returning a proper error message.
else {
log.warn("Checksum validation failed for basketToken={}", basketToken);
- return ResponseEntity.badRequest().body(null);
+ return ResponseEntity.badRequest().build();
}
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
between lines 87 and 103, the method loadShoppingBasket returns a ResponseEntity
with a null body and bad request status when checksum validation fails. Instead
of returning a null body, modify the response to include a meaningful error
message or error object in the body to provide a proper RESTful error response.
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
Outdated
Show resolved
Hide resolved
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
Outdated
Show resolved
Hide resolved
...est/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java
Outdated
Show resolved
Hide resolved
End-to-End (E2E) Test Results Summary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
♻️ Duplicate comments (5)
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java (1)
242-251
: Use Spring's task executor instead of creating a new executor.This has already been identified in past reviews. Creating a new single-thread executor for one task is inefficient. Spring provides task execution infrastructure that should be used instead.
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (1)
264-264
: Fix typo in comment.This typo has already been identified in past reviews and should be corrected.
src/main/webapp/app/sharing/sharing.component.ts (3)
80-83
: Add null safety check for token expiry calculation.The method should handle the case where
tokenValidUntil
might be undefined or invalid.
88-95
: Improve error handling to avoid exposing internal details.The error handling directly exposes server error messages to users, which can reveal sensitive information.
173-180
: Improve error handling consistency.The error handling here is inconsistent with the pattern used elsewhere in the component.
🧹 Nitpick comments (7)
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (2)
34-36
: Consider consolidating test files for better maintainability.The comment indicates this is a temporary separation for sharing-specific tests. Consider either merging with the main test file or making this separation permanent with clear documentation.
If this separation is intended to be permanent, update the comment to reflect the rationale:
-/* - * just use a separate file for sharing aspects, could be merged into programming-exercise-detail.component.spec.ts if stabilized. - */ +/** + * Dedicated test suite for sharing functionality in ProgrammingExerciseDetailComponent. + * Separated to maintain focused testing of the sharing feature integration. + */
173-184
: Enhance test assertions for better validation.The tests correctly verify the
isExportToSharingEnabled
flag but could benefit from additional assertions to ensure complete behavior validation.Consider adding assertions to verify the HTTP request details and component state:
it('should be in sharing mode', async () => { // WHEN comp.ngOnInit(); comp.programmingExercise = mockProgrammingExercise; comp.programmingExerciseBuildConfig = mockProgrammingExercise.buildConfig; const req = httpMock.expectOne({ method: 'GET', url: 'api/sharing/config/isEnabled' }); + expect(req.request.method).toBe('GET'); + expect(req.request.url).toBe('api/sharing/config/isEnabled'); req.flush(true); // THEN expect(comp.isExportToSharingEnabled).toBeTruthy(); + expect(comp.programmingExercise).toEqual(mockProgrammingExercise); });Apply similar enhancements to the negative test case for consistency.
Also applies to: 186-203
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java (1)
37-37
: Fix typo in constant name.The constant name has a typo that affects readability and professionalism.
- public static final String UNKNOWN_INSTALLATIONAME = "unknown installationame"; + public static final String UNKNOWN_INSTALLATION_NAME = "unknown installation name";src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (2)
355-386
: Extract utility method to test utility class.The
makeCourseJSONSerializable
method is a utility that modifies course entities for JSON serialization. This violates the single responsibility principle for test classes.Consider extracting this method to a dedicated test utility class like
CourseTestUtil
orJsonSerializationTestUtil
to promote reusability across test classes and improve maintainability.public static void makeCourseJSONSerializable(Course course) { // existing implementation }
223-248
: Consider extracting checksum utility methods.The
parseParamsToMap
,calculateCorrectChecksum
, andaddCorrectChecksum
methods are utility functions that could be reused across other sharing-related tests.Consider extracting these to a shared test utility class like
SharingTestUtil
to promote code reuse and reduce duplication across test classes in the sharing module.src/main/webapp/app/sharing/sharing.component.ts (2)
109-111
: Consider more descriptive method name and validation.The method name
courseId()
could be more descriptive, and there's no validation for course selection state.Consider renaming and adding validation:
- courseId(): number { - return this.selectedCourse?.id ?? 0; - } + getSelectedCourseId(): number { + if (!this.selectedCourse) { + throw new Error('No course selected'); + } + return this.selectedCourse.id; + }
126-128
: Fix return type annotation for trackId method.The trackId method should explicitly return a number type to match Angular's TrackByFunction interface requirements.
- trackId(index: number, item: Course) { + trackId(index: number, item: Course): number { return item.id; }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (22)
src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
(4 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java
(1 hunks)src/main/webapp/app/app.routes.ts
(1 hunks)src/main/webapp/app/environments/environment.override.ts
(1 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
(1 hunks)src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.html
(1 hunks)src/main/webapp/app/sharing/sharing.component.spec.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/sharing.model.ts
(1 hunks)src/main/webapp/app/sharing/sharing.scss
(1 hunks)src/main/webapp/i18n/en/sharing.json
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- src/main/webapp/app/environments/environment.override.ts
- src/main/webapp/app/sharing/sharing.component.html
- src/main/webapp/app/sharing/sharing.model.ts
🚧 Files skipped from review as they are similar to previous changes (15)
- src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
- src/main/webapp/i18n/en/sharing.json
- src/main/webapp/app/app.routes.ts
- src/main/webapp/app/sharing/sharing.scss
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java
- src/main/webapp/app/sharing/sharing.component.spec.ts
- src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java
- src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
🧰 Additional context used
📓 Path-based instructions (3)
`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:PascalC...
src/main/webapp/app/sharing/sharing.component.ts
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...
src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
`src/main/java/**/*.java`: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,de...
src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
🪛 GitHub Check: client-tests-selected
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
[failure] 49-49:
'submissionPolicyServiceStub' is declared but its value is never read.
[failure] 48-48:
'profileServiceStub' is declared but its value is never read.
[failure] 47-47:
'gitDiffReportStub' is declared but its value is never read.
[failure] 46-46:
'statisticsServiceStub' is declared but its value is never read.
[failure] 42-42:
'alertService' is declared but its value is never read.
[failure] 30-30:
Cannot find module 'app/entities/programming/build-log-statistics-dto' or its corresponding type declarations.
[failure] 27-27:
'"app/programming/shared/services/programming-language-feature/programming-language-feature.service"' has no exported member named 'ProgrammingLanguageFeature'. Did you mean 'ProgrammingLanguageFeatureService'?
[failure] 26-26:
Cannot find module 'app/shared/layouts/profiles/profile-info.model' or its corresponding type declarations.
[failure] 24-24:
Cannot find module 'app/entities/participation/solution-programming-exercise-participation.model' or its corresponding type declarations.
[failure] 23-23:
Cannot find module 'app/entities/participation/template-programming-exercise-participation.model' or its corresponding type declarations.
🪛 GitHub Check: client-tests
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
[failure] 49-49:
'submissionPolicyServiceStub' is declared but its value is never read.
[failure] 48-48:
'profileServiceStub' is declared but its value is never read.
[failure] 47-47:
'gitDiffReportStub' is declared but its value is never read.
[failure] 46-46:
'statisticsServiceStub' is declared but its value is never read.
[failure] 42-42:
'alertService' is declared but its value is never read.
[failure] 30-30:
Cannot find module 'app/entities/programming/build-log-statistics-dto' or its corresponding type declarations.
[failure] 27-27:
'"app/programming/shared/services/programming-language-feature/programming-language-feature.service"' has no exported member named 'ProgrammingLanguageFeature'. Did you mean 'ProgrammingLanguageFeatureService'?
[failure] 26-26:
Cannot find module 'app/shared/layouts/profiles/profile-info.model' or its corresponding type declarations.
[failure] 24-24:
Cannot find module 'app/entities/participation/solution-programming-exercise-participation.model' or its corresponding type declarations.
[failure] 23-23:
Cannot find module 'app/entities/participation/template-programming-exercise-participation.model' or its corresponding type declarations.
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: client-style
- GitHub Check: Analyse
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
🔇 Additional comments (3)
src/main/webapp/app/sharing/sharing.component.ts (3)
1-21
: LGTM - Clean imports and proper dependency structure.The imports are well-organized and follow Angular conventions. Good use of standalone component imports and proper separation of concerns.
25-31
: LGTM - Proper standalone component configuration.The component decorator correctly configures the standalone component with all necessary imports. The selector follows kebab-case naming convention as required.
33-62
: LGTM - Well-structured component properties.Properties follow camelCase naming convention and avoid private prefixes as required by the coding guidelines. Good use of TypeScript typing and proper initialization.
...app/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
Outdated
Show resolved
Hide resolved
...app/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
Outdated
Show resolved
Hide resolved
...app/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
Outdated
Show resolved
Hide resolved
public SharingPluginConfig getPluginConfig(URL apiBaseUrl, Optional<String> installationName) { | ||
this.sharingApiBaseUrl = apiBaseUrl; | ||
try { | ||
this.installationName = installationName.orElse(InetAddress.getLocalHost().getCanonicalHostName()); | ||
} | ||
catch (UnknownHostException e) { | ||
this.installationName = UNKNOWN_INSTALLATIONAME; | ||
} | ||
SharingPluginConfig.Action action = new SharingPluginConfig.Action("Import", "/sharing/import", actionName, | ||
"metadata.format.stream().anyMatch(entry->entry=='artemis' || entry=='Artemis').get()"); | ||
lastHealthStati.add(new HealthStatus("Delivered Sharing Config Status to " + apiBaseUrl)); | ||
lastHealthStati.setLastConnect(); | ||
return new SharingPluginConfig("Artemis Sharing Connector", new SharingPluginConfig.Action[] { action }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider thread safety for getPluginConfig method.
This method mutates instance state (sharingApiBaseUrl
and installationName
) which could be problematic in concurrent scenarios, especially in a Spring service that's typically singleton-scoped.
Consider making this method thread-safe by either:
- Synchronizing the method
- Using local variables and returning them in the config object
- Making the state immutable after initialization
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
around lines 197 to 210, the getPluginConfig method modifies instance variables
sharingApiBaseUrl and installationName, which risks thread safety in a singleton
Spring service. To fix this, avoid mutating instance state inside this method by
using local variables for apiBaseUrl and installationName instead, and construct
the SharingPluginConfig object solely from these locals. Remove or refactor any
instance state changes to ensure the method is thread-safe and side-effect free.
public boolean validate(String apiKey) { | ||
if (apiKey == null || apiKey.length() > 200) { | ||
// this is just in case, somebody tries an attack | ||
lastHealthStati.add(new HealthStatus("Failed api Key validation")); | ||
|
||
return false; | ||
} | ||
Pattern p = Pattern.compile("Bearer\\s(.+)"); | ||
Matcher m = p.matcher(apiKey); | ||
if (m.matches()) { | ||
apiKey = m.group(1); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve API key validation robustness.
The current validation has some weaknesses:
- The hardcoded length limit of 200 seems arbitrary
- The regex pattern could be more robust
public boolean validate(String apiKey) {
- if (apiKey == null || apiKey.length() > 200) {
+ if (apiKey == null || apiKey.trim().isEmpty() || apiKey.length() > 500) {
// this is just in case, somebody tries an attack
lastHealthStati.add(new HealthStatus("Failed api Key validation"));
return false;
}
- Pattern p = Pattern.compile("Bearer\\s(.+)");
+ Pattern p = Pattern.compile("^Bearer\\s+(.+)$");
Matcher m = p.matcher(apiKey);
if (m.matches()) {
apiKey = m.group(1);
}
📝 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.
public boolean validate(String apiKey) { | |
if (apiKey == null || apiKey.length() > 200) { | |
// this is just in case, somebody tries an attack | |
lastHealthStati.add(new HealthStatus("Failed api Key validation")); | |
return false; | |
} | |
Pattern p = Pattern.compile("Bearer\\s(.+)"); | |
Matcher m = p.matcher(apiKey); | |
if (m.matches()) { | |
apiKey = m.group(1); | |
} | |
public boolean validate(String apiKey) { | |
- if (apiKey == null || apiKey.length() > 200) { | |
+ if (apiKey == null || apiKey.trim().isEmpty() || apiKey.length() > 500) { | |
// this is just in case, somebody tries an attack | |
lastHealthStati.add(new HealthStatus("Failed api Key validation")); | |
return false; | |
} | |
- Pattern p = Pattern.compile("Bearer\\s(.+)"); | |
+ Pattern p = Pattern.compile("^Bearer\\s+(.+)$"); | |
Matcher m = p.matcher(apiKey); | |
if (m.matches()) { | |
apiKey = m.group(1); | |
} |
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
between lines 218 and 230, improve the API key validation by replacing the
arbitrary length limit of 200 with a configurable or more reasonable limit based
on expected API key lengths. Also, enhance the regex pattern to more strictly
and clearly match the "Bearer " prefix followed by a valid token, ensuring it
does not allow invalid formats or whitespace issues. Adjust the validation logic
accordingly to handle these improvements.
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
Show resolved
Hide resolved
@Test | ||
@WithMockUser(username = INSTRUCTORNAME + "1", roles = "INSTRUCTOR") | ||
void importExerciseCompleteProcess() throws Exception { | ||
userUtilService.addInstructor("Sharing", INSTRUCTORNAME); // unfortunatelly this utility extends the username by "1" | ||
User instructor = userUtilService.getUserByLogin(INSTRUCTORNAME + "1"); | ||
|
||
ObjectMapper objectMapper = new ObjectMapper().registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); | ||
|
||
String basketToken = importBasket(); | ||
|
||
byte[] zippedBytes = IOUtils.toByteArray(Objects.requireNonNull(this.getClass().getResource("./basket/sampleExercise.zip")).openStream()); | ||
URI basketURI = new URI(SharingPlatformMockProvider.SHARING_BASEURL_PLUGIN + "/basket/" + basketToken + "/repository/0?format=artemis"); | ||
MockRestServiceServer.MockRestServiceServerBuilder builder = MockRestServiceServer.bindTo(restTemplate); | ||
builder.ignoreExpectOrder(true); | ||
mockServer = builder.build(); | ||
|
||
final ResponseActions responseActions = mockServer.expect(ExpectedCount.once(), requestTo(basketURI)).andExpect(method(HttpMethod.GET)); | ||
responseActions.andRespond(MockRestResponseCreators.withSuccess(zippedBytes, MediaType.APPLICATION_OCTET_STREAM)); | ||
|
||
SharingInfoDTO sharingInfo = new SharingInfoDTO(); | ||
sharingInfo.setApiBaseURL(SharingPlatformMockProvider.SHARING_BASEURL_PLUGIN); | ||
sharingInfo.setReturnURL(TEST_RETURN_URL); | ||
sharingInfo.setBasketToken(basketToken); | ||
sharingInfo.setChecksum(calculateCorrectChecksum("returnURL", TEST_RETURN_URL, "apiBaseURL", sharingConnectorService.getSharingApiBaseUrlOrNull().toString())); | ||
|
||
Course course1 = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndTestCases(); | ||
|
||
// get Exercise Details | ||
|
||
MvcResult resultED = restMockMvc | ||
.perform(post("/api/sharing/import/basket/exerciseDetails").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(sharingInfo)) | ||
.accept(MediaType.APPLICATION_JSON)) /* .andDo(print()) */ | ||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); | ||
String contentED = resultED.getResponse().getContentAsString(); | ||
ObjectMapper programmingExerciseObjectMapper = new ObjectMapper(); | ||
programmingExerciseObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); | ||
programmingExerciseObjectMapper.findAndRegisterModules(); | ||
|
||
ProgrammingExercise exercise = programmingExerciseObjectMapper.readerFor(ProgrammingExercise.class).readValue(contentED); | ||
assertThat(exercise.getTitle()).isEqualTo("JUnit IO Tests"); | ||
assertThat(exercise.getProgrammingLanguage()).isEqualTo(ProgrammingLanguage.JAVA); | ||
|
||
// get Problem Statement | ||
MvcResult resultPS = restMockMvc.perform(post("/api/sharing/import/basket/problemStatement").contentType(MediaType.APPLICATION_JSON) | ||
.content(objectMapper.writeValueAsString(sharingInfo)).accept(MediaType.APPLICATION_JSON)).andExpect(content().contentType(MediaType.TEXT_PLAIN)) | ||
.andExpect(status().isOk()).andReturn(); | ||
// Zip file is cached, no extra request to sharing platform required! | ||
String contentPS = resultPS.getResponse().getContentAsString(); | ||
assertThat(contentPS).startsWith("# Simpler IO Test"); | ||
|
||
SharingSetupInfo setupInfo = new SharingSetupInfo(exercise, course1, sharingInfo); | ||
|
||
// last step: do Exercise Import | ||
makeCourseJSONSerializable(course1); | ||
// just deactivate programmingLanguageFeatureService validation | ||
ProgrammingLanguageFeature trivialProgrammingLanguageFeatures = new ProgrammingLanguageFeature(exercise.getProgrammingLanguage(), true, false, false, true, false, | ||
List.of(ProjectType.PLAIN_MAVEN), false); | ||
when(programmingLanguageFeatureService.getProgrammingLanguageFeatures(any())).thenReturn(trivialProgrammingLanguageFeatures); | ||
// mock gitService et all. | ||
when(versionControlService.checkIfProjectExists(eq(exercise.getProjectKey()), eq(exercise.getProjectName()))).thenReturn(false); | ||
when(continuousIntegrationService.checkIfProjectExists(eq(exercise.getProjectKey()), eq(exercise.getProjectName()))).thenReturn(null); | ||
// when(continuousIntegrationService.createProjectForExercise(any())); | ||
when(continuousIntegrationService.copyBuildPlan(any(), anyString(), any(), anyString(), anyString(), anyBoolean())).thenReturn(""); | ||
// when(continuousIntegrationService.givePlanPermissions(any(), anyString())) | ||
// when(continuousIntegrationService.enablePlan(any(), any())); | ||
|
||
when(versionControlService.getDefaultBranchOfArtemis()).thenReturn("main"); | ||
when(versionControlService.getCloneRepositoryUri(eq(exercise.getProjectKey()), any())).thenReturn(new VcsRepositoryUri("http://some.cloneurl")); | ||
|
||
doAnswer(invocation -> { | ||
VcsRepositoryUri uri = invocation.getArgument(0, VcsRepositoryUri.class); | ||
Repository mockedRepository = Mockito.mock(Repository.class); | ||
Mockito.when(mockedRepository.getLocalPath()).thenReturn(Files.createTempDirectory("sharingImportTest" + uri.hashCode())); | ||
return mockedRepository; | ||
}).when(gitService).getOrCheckoutRepository(any(), anyBoolean()); | ||
|
||
doNothing().when(gitService).stageAllChanges(any()); | ||
doNothing().when(gitService).commitAndPush(any(), anyString(), anyBoolean(), any()); | ||
doReturn(List.of()).when(gitService).getFiles(any()); | ||
doAnswer(invocation -> ObjectId.fromString("419a93c002688aeb7a5fd3badada7f263c1926ba")).when(gitService).getLastCommitHash(any()); | ||
// doNothing().when(continuousIntegrationTriggerService).triggerBuild(any()); | ||
|
||
String setupInfoJsonString = objectMapper.writeValueAsString(setupInfo); | ||
MvcResult resultPE = restMockMvc | ||
.perform(post("/api/sharing/setup-import").contentType(MediaType.APPLICATION_JSON).content(setupInfoJsonString).accept(MediaType.APPLICATION_JSON)) | ||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); | ||
|
||
// finally cleanup the cache | ||
exerciseSharingService.getRepositoryCache().asMap().forEach((key, value) -> { | ||
exerciseSharingService.getRepositoryCache().invalidate(key); | ||
}); | ||
exerciseSharingService.getRepositoryCache().cleanUp(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor large test method for better maintainability.
The importExerciseCompleteProcess
method is quite long (93 lines) and covers multiple responsibilities, violating the single responsibility principle mentioned in coding guidelines.
Consider breaking this test into smaller, focused test methods:
shouldImportBasketToken()
- for basket import verificationshouldFetchExerciseDetails()
- for exercise details validationshouldFetchProblemStatement()
- for problem statement validationshouldCompleteExerciseSetup()
- for final setup import
Each method should focus on a specific aspect of the import process, making tests easier to understand and maintain.
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
around lines 261 to 353, the importExerciseCompleteProcess test method is too
long and handles multiple responsibilities. Refactor by splitting this method
into smaller, focused test methods: one for verifying basket token import, one
for validating exercise details fetching, one for checking problem statement
retrieval, and one for completing the exercise setup import. Each new test
should isolate and test a specific part of the process to improve readability
and maintainability.
End-to-End (E2E) Test Results Summary |
2 similar comments
End-to-End (E2E) Test Results Summary |
End-to-End (E2E) Test Results Summary |
562e101
to
8f6ada8
Compare
End-to-End (E2E) Test Results Summary |
There was a problem hiding this 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
♻️ Duplicate comments (11)
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java (6)
259-259
: Remove server-side HTML escaping for Markdown content.Server-side escaping of
problemStatement
will break Markdown formatting when rendered by the client-side Markdown renderer. The client should handle sanitization.- return Objects.requireNonNullElse(org.springframework.web.util.HtmlUtils.htmlEscape(problemStatement), "No Problem Statement found!"); + return Objects.requireNonNullElse(problemStatement, "No Problem Statement found!");
370-370
: Use proper URL-safe Base64 encoding instead of manual padding removal.Manually removing Base64 padding can invalidate the encoding. Use URL-safe Base64 encoder without padding for clean, valid encoding.
- String tokenInB64 = Base64.getEncoder().encodeToString(token.getBytes()).replaceAll("=+$", ""); + String tokenInB64 = Base64.getUrlEncoder().withoutPadding().encodeToString(token.getBytes());
415-416
: Don't return empty array on cryptographic failures.Returning an empty byte array on HMAC generation failure is a security vulnerability as it produces predictable output.
catch (NoSuchAlgorithmException | InvalidKeyException e) { - return Base64.getEncoder().encodeToString(new byte[] {}); + throw new SharingException("Failed to generate HMAC", e); }
404-406
: Add null check for pre-shared key.The PSK retrieved from
getSharingApiKeyOrNull()
could be null if not configured, causing a NullPointerException.String psk = sharingConnectorService.getSharingApiKeyOrNull(); + if (psk == null) { + throw new IllegalStateException("Sharing API key is not configured"); + } SecretKeySpec secretKeySpec = new SecretKeySpec(psk.getBytes(StandardCharsets.UTF_8), algorithm);
429-432
: Use proper URL decoding instead of manual character replacement.Manual space-to-plus conversion indicates improper URL decoding. This should be handled at the REST endpoint level with proper
@RequestParam
configuration.
443-449
: Strengthen token validation against directory traversal.The decoded token is used directly in path construction, which could allow directory traversal attacks if the token contains path separators.
String decodedToken = new String(Base64.getDecoder().decode(b64Token)); + // Additional validation after decoding + if (decodedToken.contains("..") || decodedToken.contains("/") || decodedToken.contains("\\")) { + log.warn("Token contains invalid path characters: {}", b64Token); + return null; + } Path parent = Paths.get(repoDownloadClonePath, decodedToken + ".zip");src/main/webapp/app/sharing/sharing.component.spec.ts (2)
133-133
: Fix typo in error message."Bakset" should be "Basket".
- { message: 'Bakset not found' }, // error body + { message: 'Basket not found' }, // error body
147-147
: MissingtoBeBetween()
custom matcher definition.The
.toBeBetween()
matcher used here isn't defined anywhere in the codebase, causing test failures.Create a custom matcher in your test helpers:
beforeAll(() => { jasmine.addMatchers({ toBeBetween: () => ({ compare(actual: Date, floor: Date, ceiling: Date) { const pass = actual >= floor && actual <= ceiling; return { pass, message: pass ? `Expected ${actual} not to be between ${floor} and ${ceiling}` : `Expected ${actual} to be between ${floor} and ${ceiling}`, }; }, }), }); });src/main/webapp/app/sharing/sharing.component.ts (2)
80-84
: Add null safety check for token expiry calculation.The method should handle the case where
tokenValidUntil
might be undefined or invalid.getTokenExpiryDate(): Date { - if (this.shoppingBasket) { + if (this.shoppingBasket?.tokenValidUntil) { return new Date(this.shoppingBasket.tokenValidUntil); } return new Date(); }
93-93
: Avoid exposing internal error messages to users.Directly concatenating
res.message
in error alerts can expose sensitive internal information to users.- error: (res: HttpErrorResponse) => this.alertService.error('Cannot load courses: ' + res.message), + error: (res: HttpErrorResponse) => this.alertService.error('artemisApp.sharing.error.loadingCourses'),src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (1)
23-30
: Fix import path errors that prevent compilation.Multiple imports have incorrect paths causing compilation failures. Based on the relevant code snippets, here are the correct paths:
-import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; -import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; +import { TemplateProgrammingExerciseParticipation } from 'app/exercise/shared/entities/participation/template-programming-exercise-participation.model'; +import { SolutionProgrammingExerciseParticipation } from 'app/exercise/shared/entities/participation/solution-programming-exercise-participation.model'; -import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; -import { ProgrammingLanguageFeature, ProgrammingLanguageFeatureService } from 'app/programming/shared/services/programming-language-feature/programming-language-feature.service'; +import { ProfileInfo, ProgrammingLanguageFeature } from 'app/core/layouts/profiles/profile-info.model'; +import { ProgrammingLanguageFeatureService } from 'app/programming/shared/services/programming-language-feature/programming-language-feature.service'; -import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; +// Remove this unused import as BuildLogStatisticsDTO is not used in the test🧰 Tools
🪛 GitHub Check: client-tests
[failure] 30-30:
Cannot find module 'app/entities/programming/build-log-statistics-dto' or its corresponding type declarations.
[failure] 30-30:
'BuildLogStatisticsDTO' is declared but its value is never read.
[failure] 27-27:
'"app/programming/shared/services/programming-language-feature/programming-language-feature.service"' has no exported member named 'ProgrammingLanguageFeature'. Did you mean 'ProgrammingLanguageFeatureService'?
[failure] 26-26:
Cannot find module 'app/shared/layouts/profiles/profile-info.model' or its corresponding type declarations.
[failure] 24-24:
Cannot find module 'app/entities/participation/solution-programming-exercise-participation.model' or its corresponding type declarations.
[failure] 23-23:
Cannot find module 'app/entities/participation/template-programming-exercise-participation.model' or its corresponding type declarations.🪛 GitHub Check: client-tests-selected
[failure] 30-30:
Cannot find module 'app/entities/programming/build-log-statistics-dto' or its corresponding type declarations.
[failure] 30-30:
'BuildLogStatisticsDTO' is declared but its value is never read.
[failure] 27-27:
'"app/programming/shared/services/programming-language-feature/programming-language-feature.service"' has no exported member named 'ProgrammingLanguageFeature'. Did you mean 'ProgrammingLanguageFeatureService'?
[failure] 26-26:
Cannot find module 'app/shared/layouts/profiles/profile-info.model' or its corresponding type declarations.
[failure] 24-24:
Cannot find module 'app/entities/participation/solution-programming-exercise-participation.model' or its corresponding type declarations.
[failure] 23-23:
Cannot find module 'app/entities/participation/template-programming-exercise-participation.model' or its corresponding type declarations.
🧹 Nitpick comments (2)
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (2)
126-129
: Remove duplicate service mock assignment.The
profileService.getProfileInfo
method is mocked twice, creating redundant assignments.jest.spyOn(exerciseService, 'getDiffReport').mockReturnValue(of(gitDiffReport)); jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(profileInfo)); jest.spyOn(submissionPolicyService, 'getSubmissionPolicyOfProgrammingExercise').mockReturnValue(of(undefined)); - - jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(profileInfo));🧰 Tools
🪛 GitHub Check: client-tests
[failure] 129-129:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 126-126:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.🪛 GitHub Check: client-tests-selected
[failure] 129-129:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 126-126:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
46-58
: Fix type assertion issue in mock object.The mock object doesn't fully match the
ProgrammingExercise
type structure, causing compilation warnings.- const mockProgrammingExercise = { + const mockProgrammingExercise: Partial<ProgrammingExercise> = { id: 1, categories: [{ category: 'Important' }], templateParticipation: { id: 1, } as TemplateProgrammingExerciseParticipation, solutionParticipation: { id: 2, } as SolutionProgrammingExerciseParticipation, buildConfig: { buildTool: 'GRADLE', }, - } as ProgrammingExercise; + };🧰 Tools
🪛 GitHub Check: client-tests
[failure] 46-46:
Conversion of type '{ id: number; categories: { category: string; }[]; templateParticipation: TemplateProgrammingExerciseParticipation; solutionParticipation: SolutionProgrammingExerciseParticipation; buildConfig: { ...; }; }' to type 'ProgrammingExercise' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.🪛 GitHub Check: client-tests-selected
[failure] 46-46:
Conversion of type '{ id: number; categories: { category: string; }[]; templateParticipation: TemplateProgrammingExerciseParticipation; solutionParticipation: SolutionProgrammingExerciseParticipation; buildConfig: { ...; }; }' to type 'ProgrammingExercise' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (16)
.github/workflows/build.yml
is excluded by!**/*.yml
docs/admin/setup/sharing/sharingButtonArtemis.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharingButtonSharing.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharing_health1.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharing_health2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/artemis_import.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/artemis_import2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_export.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_metadata.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_metadata2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_namespace.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_search.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_success.png
is excluded by!**/*.png
,!**/*.png
src/main/resources/config/application-dev.yml
is excluded by!**/*.yml
src/main/resources/config/application-sharing.yml
is excluded by!**/*.yml
src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleExercise.zip
is excluded by!**/*.zip
,!**/*.zip
📒 Files selected for processing (54)
build.gradle
(2 hunks)docker/artemis/config/dev-local-vc-local-ci.env
(1 hunks)docs/admin/extension-services.rst
(1 hunks)docs/admin/setup/sharing.rst
(1 hunks)docs/index.rst
(1 hunks)docs/user/sharing.rst
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/Constants.java
(3 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/RestTemplateConfiguration.java
(2 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/dto/SharingInfoDTO.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
(4 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ProgrammingExerciseImportFromSharingService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
(1 hunks)src/main/webapp/app/app.routes.ts
(1 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.html
(1 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts
(7 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
(1 hunks)src/main/webapp/app/programming/manage/exercise/programming-exercise.component.ts
(1 hunks)src/main/webapp/app/programming/manage/programming-exercise-management.route.ts
(1 hunks)src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
(1 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-creation-config.ts
(1 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-update.component.html
(3 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
(17 hunks)src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/search-result-dto.model.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.html
(1 hunks)src/main/webapp/app/sharing/sharing.component.spec.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/sharing.model.ts
(1 hunks)src/main/webapp/app/sharing/sharing.route.ts
(1 hunks)src/main/webapp/app/sharing/sharing.scss
(1 hunks)src/main/webapp/i18n/de/programmingExercise.json
(1 hunks)src/main/webapp/i18n/de/sharing.json
(1 hunks)src/main/webapp/i18n/en/programmingExercise.json
(1 hunks)src/main/webapp/i18n/en/sharing.json
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationIndependentTest.java
(2 hunks)src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java
(3 hunks)src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleBasket.json
(1 hunks)
✅ Files skipped from review due to trivial changes (8)
- docs/admin/extension-services.rst
- src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationIndependentTest.java
- build.gradle
- src/main/webapp/i18n/en/programmingExercise.json
- src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
- src/main/webapp/app/sharing/sharing.component.html
- docs/admin/setup/sharing.rst
- src/main/webapp/app/sharing/search-result-dto.model.ts
🚧 Files skipped from review as they are similar to previous changes (39)
- docs/index.rst
- src/main/webapp/app/programming/manage/exercise/programming-exercise.component.ts
- src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.html
- src/main/webapp/app/programming/manage/update/programming-exercise-creation-config.ts
- src/main/webapp/app/programming/manage/update/programming-exercise-update.component.html
- src/main/webapp/i18n/en/sharing.json
- src/main/webapp/app/app.routes.ts
- src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
- src/main/webapp/app/programming/manage/programming-exercise-management.route.ts
- src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java
- src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleBasket.json
- src/main/webapp/i18n/de/programmingExercise.json
- src/main/webapp/i18n/de/sharing.json
- src/main/java/de/tum/cit/aet/artemis/core/config/RestTemplateConfiguration.java
- src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingServiceTest.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
- src/main/webapp/app/sharing/sharing.model.ts
- src/main/webapp/app/sharing/sharing.scss
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
- src/main/webapp/app/sharing/sharing.route.ts
- src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ProgrammingExerciseImportFromSharingService.java
- docs/user/sharing.rst
- src/main/java/de/tum/cit/aet/artemis/core/config/Constants.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java
- src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
- src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
- src/main/java/de/tum/cit/aet/artemis/core/dto/SharingInfoDTO.java
- src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java
- src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
🧰 Additional context used
📓 Path-based instructions (3)
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...
src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
`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:PascalC...
src/main/webapp/app/sharing/sharing.component.ts
src/main/webapp/app/sharing/sharing.component.spec.ts
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
`src/main/java/**/*.java`: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,de...
src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
🧠 Learnings (2)
📓 Common learnings
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10989
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java:122-135
Timestamp: 2025-06-06T14:47:54.274Z
Learning: In Artemis sharing platform integration (under "sharing" profile), endpoints in ExerciseSharingResource use checksum-based authentication with shared secrets instead of @PreAuthorize annotations. These endpoints (like getProblemStatement, getExerciseDetails, loadShoppingBasket) validate security through sharingInfo.checkChecksum() calls rather than role-based access control, as they serve as API interfaces for external sharing platform communication.
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts (1)
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10989
File: src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts:1283-1303
Timestamp: 2025-06-06T14:12:04.548Z
Learning: Wallenstein61 prefers to minimize interference with original code when implementing new features, even if it means not following optimal patterns like avoiding nested subscriptions. This approach prioritizes compatibility and reduced risk over strict adherence to best practices in large established codebases.
🧬 Code Graph Analysis (3)
src/main/webapp/app/sharing/sharing.component.spec.ts (4)
src/test/javascript/spec/helpers/mocks/service/mock-alert.service.ts (1)
MockAlertService
(3-8)src/test/javascript/spec/helpers/mocks/mock-router.ts (1)
MockRouter
(5-30)src/main/webapp/app/sharing/sharing.model.ts (1)
ShoppingBasket
(50-54)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts (1)
src/main/webapp/app/programming/manage/update/programming-exercise-creation-config.ts (1)
ProgrammingExerciseCreationConfig
(7-56)
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (4)
src/main/webapp/app/core/layouts/profiles/profile-info.model.ts (1)
ProgrammingLanguageFeature
(115-124)src/test/javascript/spec/helpers/mocks/service/mock-ngb-modal.service.ts (1)
MockNgbModalService
(1-5)src/test/javascript/spec/helpers/mocks/mock-router.ts (1)
MockRouter
(5-30)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)
🪛 GitHub Check: client-tests
src/main/webapp/app/sharing/sharing.component.spec.ts
[failure] 2-2:
Cannot find module '../../helpers/mocks/service/mock-translate.service' or its corresponding type declarations.
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
[failure] 129-129:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 126-126:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 46-46:
Conversion of type '{ id: number; categories: { category: string; }[]; templateParticipation: TemplateProgrammingExerciseParticipation; solutionParticipation: SolutionProgrammingExerciseParticipation; buildConfig: { ...; }; }' to type 'ProgrammingExercise' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
[failure] 30-30:
Cannot find module 'app/entities/programming/build-log-statistics-dto' or its corresponding type declarations.
[failure] 30-30:
'BuildLogStatisticsDTO' is declared but its value is never read.
[failure] 27-27:
'"app/programming/shared/services/programming-language-feature/programming-language-feature.service"' has no exported member named 'ProgrammingLanguageFeature'. Did you mean 'ProgrammingLanguageFeatureService'?
[failure] 26-26:
Cannot find module 'app/shared/layouts/profiles/profile-info.model' or its corresponding type declarations.
[failure] 24-24:
Cannot find module 'app/entities/participation/solution-programming-exercise-participation.model' or its corresponding type declarations.
[failure] 23-23:
Cannot find module 'app/entities/participation/template-programming-exercise-participation.model' or its corresponding type declarations.
🪛 GitHub Check: client-tests-selected
src/main/webapp/app/sharing/sharing.component.spec.ts
[failure] 2-2:
Cannot find module '../../helpers/mocks/service/mock-translate.service' or its corresponding type declarations.
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
[failure] 129-129:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 126-126:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 46-46:
Conversion of type '{ id: number; categories: { category: string; }[]; templateParticipation: TemplateProgrammingExerciseParticipation; solutionParticipation: SolutionProgrammingExerciseParticipation; buildConfig: { ...; }; }' to type 'ProgrammingExercise' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
[failure] 30-30:
Cannot find module 'app/entities/programming/build-log-statistics-dto' or its corresponding type declarations.
[failure] 30-30:
'BuildLogStatisticsDTO' is declared but its value is never read.
[failure] 27-27:
'"app/programming/shared/services/programming-language-feature/programming-language-feature.service"' has no exported member named 'ProgrammingLanguageFeature'. Did you mean 'ProgrammingLanguageFeatureService'?
[failure] 26-26:
Cannot find module 'app/shared/layouts/profiles/profile-info.model' or its corresponding type declarations.
[failure] 24-24:
Cannot find module 'app/entities/participation/solution-programming-exercise-participation.model' or its corresponding type declarations.
[failure] 23-23:
Cannot find module 'app/entities/participation/template-programming-exercise-participation.model' or its corresponding type declarations.
🪛 ast-grep (0.38.1)
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
[warning] 408-408: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Mac.getInstance(algorithm)
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html
(use-of-aes-ecb-java)
[warning] 408-408: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Mac.getInstance(algorithm)
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA
(desede-is-deprecated-java)
🪛 dotenv-linter (3.3.0)
docker/artemis/config/dev-local-vc-local-ci.env
[warning] 5-5: [IncorrectDelimiter] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key has incorrect delimiter
[warning] 5-5: [KeyWithoutValue] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key should be with a value or have an equal sign
[warning] 5-5: [LowercaseKey] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key should be in uppercase
⏰ Context from checks skipped due to timeout of 90000ms (10)
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
- GitHub Check: client-style
- GitHub Check: server-tests
- GitHub Check: server-style
- GitHub Check: Analyse
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
🔇 Additional comments (9)
docker/artemis/config/dev-local-vc-local-ci.env (1)
5-5
: LGTM: Sharing profile correctly added.The addition of the "sharing" profile to
SPRING_PROFILES_ACTIVE
properly enables the sharing platform integration feature as documented.🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 5-5: [IncorrectDelimiter] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key has incorrect delimiter
[warning] 5-5: [KeyWithoutValue] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key should be with a value or have an equal sign
[warning] 5-5: [LowercaseKey] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key should be in uppercase
src/main/webapp/app/sharing/sharing.component.ts (1)
170-209
: LGTM: Initialization and error handling improvements.The refactoring of
ngOnInit()
with parameter validation, separateloadSharedExercises()
method, and improved navigation error handling addresses the previous concerns well.src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java (1)
41-64
: Well-structured test methods following best practices.The test methods properly validate the
SharingMultipartZipFile
functionality with appropriate resource management using try-with-resources and comprehensive assertions for file properties and exception handling.src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts (6)
10-10
: Well-integrated sharing service and properties.The integration of
ProgrammingExerciseSharingService
andSharingInfo
follows established patterns in the component with proper service injection and property initialization.Also applies to: 43-43, 82-96, 152-152, 207-207
471-471
: Proper route segment detection for sharing import.The route parsing correctly identifies the
import-from-sharing
segment and triggers the appropriate setup method, following the same pattern used for other import types.Also applies to: 480-482
132-134
: Appropriate UI field visibility control for sharing.The conditional display of the SHORT_NAME field when sharing info is present ensures proper user experience during sharing import workflows.
352-352
: Consistent conditional checks for sharing import.The
isImportFromSharing
flag is properly integrated into existing conditional logic throughout the component, maintaining consistency with other import types.Also applies to: 373-373, 520-520, 645-645, 722-722
771-780
: Effective sharing import save implementation.The save logic for sharing import properly calls the sharing service, handles success/error scenarios, and integrates well with the existing save flow.
1308-1308
: Complete configuration integration.The
isImportFromSharing
flag is properly included in the programming exercise creation configuration, ensuring consistent state throughout the component tree.
@@ -0,0 +1,203 @@ | |||
import { TranslateDirective } from 'app/shared/language/translate.directive'; | |||
import { MockTranslateService, TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Fix import path for mock translate service.
The import path '../../helpers/mocks/service/mock-translate.service'
is causing build failures.
🏁 Script executed:
#!/bin/bash
# Find the correct path for mock-translate.service
fd "mock-translate.service" --type f --extension ts
Length of output: 124
Fix import path in sharing.component.spec.ts
The mock-translate service has been moved under the test helpers directory. Update the import in src/main/webapp/app/sharing/sharing.component.spec.ts
to point to the correct location:
File: src/main/webapp/app/sharing/sharing.component.spec.ts
- import { MockTranslateService, TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service';
+ import { MockTranslateService, TranslatePipeMock } from '../../../../test/javascript/spec/helpers/mocks/service/mock-translate.service';
📝 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.
import { MockTranslateService, TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; | |
import { MockTranslateService, TranslatePipeMock } from '../../../../test/javascript/spec/helpers/mocks/service/mock-translate.service'; |
🧰 Tools
🪛 GitHub Check: client-tests
[failure] 2-2:
Cannot find module '../../helpers/mocks/service/mock-translate.service' or its corresponding type declarations.
🪛 GitHub Check: client-tests-selected
[failure] 2-2:
Cannot find module '../../helpers/mocks/service/mock-translate.service' or its corresponding type declarations.
🤖 Prompt for AI Agents
In src/main/webapp/app/sharing/sharing.component.spec.ts at line 2, the import
path for MockTranslateService and TranslatePipeMock is incorrect and causes
build failures. Update the import statement to use the new correct path under
the test helpers directory where the mock-translate.service.ts file now resides.
Replace the old relative path
'../../helpers/mocks/service/mock-translate.service' with the accurate path
reflecting the new location of the mock-translate service.
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
Outdated
Show resolved
Hide resolved
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
Show resolved
Hide resolved
End-to-End (E2E) Test Results Summary |
1 similar comment
End-to-End (E2E) Test Results Summary |
There was a problem hiding this 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
♻️ Duplicate comments (3)
src/main/webapp/app/sharing/sharing.component.spec.ts (3)
2-2
: Fix import path for mock translate service.The import path appears to be incorrect based on the project structure. The current relative path may not resolve properly.
Based on previous review feedback, this import path needs to be corrected to point to the actual location of the mock translate service file.
133-133
: Fix typo in error message."Bakset" should be "Basket".
- { message: 'Bakset not found' }, // error body + { message: 'Basket not found' }, // error body
146-146
: Missing custom matcher definition.The
.toBeBetween()
matcher used here is not a standard Jasmine matcher and needs to be defined.This custom matcher must be implemented and registered before it can be used in tests. Consider using standard Jasmine matchers instead:
- expect(fixture.componentInstance.getTokenExpiryDate()).toBeBetween(new Date(Date.now() - 1000), new Date(Date.now() + 1000)); + const tokenExpiryDate = fixture.componentInstance.getTokenExpiryDate(); + expect(tokenExpiryDate.getTime()).toBeGreaterThanOrEqual(Date.now() - 1000); + expect(tokenExpiryDate.getTime()).toBeLessThanOrEqual(Date.now() + 1000);
🧹 Nitpick comments (4)
src/main/webapp/app/sharing/sharing.component.spec.ts (2)
71-72
: Fix typo in course titles."testCouse" should be "testCourse" in both course objects.
- { id: 1, title: 'testCouse 1' }, - { id: 2, title: 'testCouse 2' }, + { id: 1, title: 'testCourse 1' }, + { id: 2, title: 'testCourse 2' },
186-192
: Simplify redundant error testing.The try-catch block from lines 186-192 tests the same validation error as lines 180-185. This duplication is unnecessary.
- try { - sharingInfo.validate(); - throw new Error('Error expected, got none'); - } catch (err) { - expect(err).toBeInstanceOf(Error); - expect(err.message).toBe('Basket token is required'); - }src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (2)
45-57
: Fix type assertion for mock programming exercise.The static analysis correctly identifies a type mismatch. Use proper type assertion to resolve this.
- const mockProgrammingExercise = { + const mockProgrammingExercise = { id: 1, categories: [{ category: 'Important' }], templateParticipation: { id: 1, } as TemplateProgrammingExerciseParticipation, solutionParticipation: { id: 2, } as SolutionProgrammingExerciseParticipation, buildConfig: { buildTool: 'GRADLE', }, - } as ProgrammingExercise; + } as Partial<ProgrammingExercise>;🧰 Tools
🪛 GitHub Check: client-tests
[failure] 45-45:
Conversion of type '{ id: number; categories: { category: string; }[]; templateParticipation: TemplateProgrammingExerciseParticipation; solutionParticipation: SolutionProgrammingExerciseParticipation; buildConfig: { ...; }; }' to type 'ProgrammingExercise' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.🪛 GitHub Check: client-tests-selected
[failure] 45-45:
Conversion of type '{ id: number; categories: { category: string; }[]; templateParticipation: TemplateProgrammingExerciseParticipation; solutionParticipation: SolutionProgrammingExerciseParticipation; buildConfig: { ...; }; }' to type 'ProgrammingExercise' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
125-128
: Remove duplicate mock setup.The
profileService.getProfileInfo
is mocked twice with identical configuration, creating unnecessary duplication.jest.spyOn(exerciseService, 'getDiffReport').mockReturnValue(of(gitDiffReport)); jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(profileInfo)); jest.spyOn(submissionPolicyService, 'getSubmissionPolicyOfProgrammingExercise').mockReturnValue(of(undefined)); - jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(profileInfo)); jest.spyOn(programmingLanguageFeatureService, 'getProgrammingLanguageFeature').mockReturnValue({ plagiarismCheckSupported: true, } as ProgrammingLanguageFeature);
🧰 Tools
🪛 GitHub Check: client-tests
[failure] 128-128:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 125-125:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.🪛 GitHub Check: client-tests-selected
[failure] 128-128:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 125-125:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.spec.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.ts
(1 hunks)src/test/javascript/spec/helpers/mocks/programming-exercise-creation-config-mock.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/test/javascript/spec/helpers/mocks/programming-exercise-creation-config-mock.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/webapp/app/sharing/sharing.component.ts
🧰 Additional context used
📓 Path-based instructions (1)
`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:PascalC...
src/main/webapp/app/sharing/sharing.component.spec.ts
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10989
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java:122-135
Timestamp: 2025-06-06T14:47:54.274Z
Learning: In Artemis sharing platform integration (under "sharing" profile), endpoints in ExerciseSharingResource use checksum-based authentication with shared secrets instead of @PreAuthorize annotations. These endpoints (like getProblemStatement, getExerciseDetails, loadShoppingBasket) validate security through sharingInfo.checkChecksum() calls rather than role-based access control, as they serve as API interfaces for external sharing platform communication.
🧬 Code Graph Analysis (2)
src/main/webapp/app/sharing/sharing.component.spec.ts (4)
src/test/javascript/spec/helpers/mocks/service/mock-alert.service.ts (1)
MockAlertService
(3-8)src/test/javascript/spec/helpers/mocks/mock-router.ts (1)
MockRouter
(5-30)src/main/webapp/app/sharing/sharing.model.ts (1)
ShoppingBasket
(50-54)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (4)
src/main/webapp/app/core/layouts/profiles/profile-info.model.ts (1)
ProgrammingLanguageFeature
(115-124)src/test/javascript/spec/helpers/mocks/service/mock-ngb-modal.service.ts (1)
MockNgbModalService
(1-5)src/test/javascript/spec/helpers/mocks/mock-router.ts (1)
MockRouter
(5-30)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)
🪛 GitHub Check: client-tests
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
[failure] 128-128:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 125-125:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 45-45:
Conversion of type '{ id: number; categories: { category: string; }[]; templateParticipation: TemplateProgrammingExerciseParticipation; solutionParticipation: SolutionProgrammingExerciseParticipation; buildConfig: { ...; }; }' to type 'ProgrammingExercise' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
🪛 GitHub Check: client-tests-selected
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
[failure] 128-128:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 125-125:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 45-45:
Conversion of type '{ id: number; categories: { category: string; }[]; templateParticipation: TemplateProgrammingExerciseParticipation; solutionParticipation: SolutionProgrammingExerciseParticipation; buildConfig: { ...; }; }' to type 'ProgrammingExercise' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
⏰ Context from checks skipped due to timeout of 90000ms (11)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: client-style
- GitHub Check: server-tests
- GitHub Check: Build .war artifact
- GitHub Check: server-style
- GitHub Check: Analyse
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
🔇 Additional comments (8)
src/main/webapp/app/sharing/sharing.component.spec.ts (2)
75-110
: LGTM! Comprehensive integration test.This test effectively covers the complete user flow from loading data to navigation, with proper mocking and assertions.
167-202
: Excellent validation test coverage.The test thoroughly covers both positive and negative validation scenarios for the SharingInfo model, ensuring proper error handling and validation logic.
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (6)
1-32
: Import structure looks good.The imports are well-organized and include all necessary dependencies for testing the sharing functionality.
92-132
: TestBed configuration follows best practices.The test module setup correctly provides all necessary services and mocks for testing the component.
🧰 Tools
🪛 GitHub Check: client-tests
[failure] 128-128:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 125-125:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.🪛 GitHub Check: client-tests-selected
[failure] 128-128:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
[failure] 125-125:
Argument of type 'Observable' is not assignable to parameter of type 'ProfileInfo'.
138-151
: Test structure follows Angular testing best practices.The test setup correctly uses HttpTestingController for HTTP mocking and includes proper teardown with
httpMock.verify()
.
152-163
: Test case correctly verifies sharing enabled scenario.The test properly mocks the HTTP response and validates that the component sets
isExportToSharingEnabled
to true when the API returns success.
165-182
: Test case correctly verifies sharing disabled scenario.The test properly simulates a 404 error response and validates that the component sets
isExportToSharingEnabled
to false when the sharing API is not available.
33-35
: Good decision to separate sharing tests.Creating a dedicated test file for sharing functionality helps maintain clear test organization and can be easily integrated with the main test file when the feature stabilizes.
End-to-End (E2E) Test Results Summary |
4 similar comments
End-to-End (E2E) Test Results Summary |
End-to-End (E2E) Test Results Summary |
End-to-End (E2E) Test Results Summary |
End-to-End (E2E) Test Results Summary |
End-to-End (E2E) Test Results Summary
|
End-to-End (E2E) Test Results Summary |
End-to-End (E2E) Test Results Summary
|
There was a problem hiding this 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
Plan: Pro
📒 Files selected for processing (1)
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`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:PascalC...
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10989
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java:122-135
Timestamp: 2025-06-06T14:47:54.300Z
Learning: In Artemis sharing platform integration (under "sharing" profile), endpoints in ExerciseSharingResource use checksum-based authentication with shared secrets instead of @PreAuthorize annotations. These endpoints (like getProblemStatement, getExerciseDetails, loadShoppingBasket) validate security through sharingInfo.checkChecksum() calls rather than role-based access control, as they serve as API interfaces for external sharing platform communication.
🪛 GitHub Check: client-tests
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
[failure] 759-759:
Type 'ProgrammingExercise' has no properties in common with type '{ body?: unknown; headers?: HttpHeaders | undefined; status?: number | undefined; statusText?: string | undefined; url?: string | undefined; }'.
[failure] 759-759:
Argument of type 'Observable<HttpResponse>' is not assignable to parameter of type 'Observable'.
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: server-tests
- GitHub Check: client-tests-selected
- GitHub Check: submit-gradle
- GitHub Check: Build .war artifact
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
Outdated
Show resolved
Hide resolved
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
Outdated
Show resolved
Hide resolved
End-to-End (E2E) Test Results Summary |
df8b9ea
to
d7e0690
Compare
There was a problem hiding this 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
♻️ Duplicate comments (5)
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (3)
273-274
: Typo in inline comment
unfortunatelly
→unfortunately
232-241
:⚠️ Potential issueInput-validation still missing in
parseParamsToMap
The method still trusts its var-arg input. A
null
array or odd parameter count will trigger NPE / assertion errors outside AssertJ’s scope in production code that re-uses the utility.private Map<String, String> parseParamsToMap(String... params) { - Map<String, String> paramsMap = new HashMap<>(); - assertThat(params.length % 2).isEqualTo(0).describedAs("paramList must contain even elements"); + if (params == null) { + throw new IllegalArgumentException("params cannot be null"); + } + assertThat(params.length % 2).isEqualTo(0).describedAs("paramList must contain even elements"); + Map<String, String> paramsMap = new HashMap<>(params.length / 2); for (int i = 0; i < params.length; i = i + 2) { String paramName = params[i]; String paramValue = params[i + 1]; paramsMap.put(paramName, paramValue); } return paramsMap; }
270-334
: 🛠️ Refactor suggestion
importExerciseCompleteProcess
test is still 90+ linesThe test continues to mix basket import, REST calls, Git & CI stubbing, and cache cleanup. Splitting into focused, smaller tests (e.g. shouldImportBasket , shouldRetrieveExerciseDetails, shouldSetupExercise) would improve readability and pinpoint failures faster.
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (1)
130-131
:⚠️ Potential issue
getProfileInfo
mock must return an Observable, not a raw object
ProfileService.getProfileInfo()
returns anObservable<ProfileInfo>
.
Returning the plainprofileInfo
object breaks the method’s contract and was already flagged earlier — this will re-introduce the same compilation error.- jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(profileInfo); + jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(profileInfo));src/main/webapp/app/sharing/sharing.component.spec.ts (1)
144-147
:toBeBetween()
matcher still undefined – tests will fail
Previous review already raised this; the custom Jasmine matcher is still missing.Replace with built-in assertions or register the matcher in the global test setup.
🧹 Nitpick comments (4)
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (2)
243-257
: DRY: duplicate checksum calculation logic
calculateCorrectChecksum
already encapsulates checksum generation.addCorrectChecksum
re-implements it inline. Re-use the helper to avoid divergent logic.- String checkSum = SecretChecksumCalculator.calculateChecksum(paramsToCheckSum, sharingApiKey); + String checkSum = calculateCorrectChecksum(params);
170-173
: Remove unused local variable
sampleBasket
is read but never used intestImportBasketFail
; delete the dead code to keep the test focused.- String sampleBasket = IOUtils.toString(Objects.requireNonNull(this.getClass().getResource("./basket/sampleBasket.json")), StandardCharsets.UTF_8);
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts (1)
156-166
: Redundant manual assignment ofprogrammingExercise
inside the testsAfter
ngOnInit()
the component already receives the exercise via theActivatedRoute
data; the extra linescomp.programmingExercise = mockProgrammingExercise; comp.programmingExerciseBuildConfig = mockProgrammingExercise.buildConfig;overwrite that state in every test, adding noise without changing the outcome.
Consider removing those two statements in each test case to keep the spec concise.Also applies to: 170-186, 188-205
src/main/webapp/app/sharing/sharing.component.html (1)
113-115
: Spelling / translation nit – “Platform”“Plattform” is German spelling.
For the English translation key use “Platform” to avoid inconsistency.-<span class="hidden-sm-down" jhiTranslate="artemisApp.programmingExercise.sharing.import">Import Programming Exercise from Sharing Plattform</span> +<span class="hidden-sm-down" jhiTranslate="artemisApp.programmingExercise.sharing.import">Import Programming Exercise from Sharing Platform</span>🧰 Tools
🪛 HTMLHint (1.5.0)
[error] 114-114: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
build.gradle
(2 hunks)src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java
(2 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
(4 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
(1 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
(1 hunks)src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.spec.ts
(1 hunks)src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
(1 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
(5 hunks)src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.spec.ts
(1 hunks)src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/search-result-dto.model.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.html
(1 hunks)src/main/webapp/app/sharing/sharing.component.spec.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/sharing.scss
(1 hunks)src/main/webapp/i18n/de/sharing.json
(1 hunks)src/main/webapp/i18n/en/sharing.json
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
(1 hunks)src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleBasket.json
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- build.gradle
- src/main/webapp/app/sharing/search-result-dto.model.ts
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java
🚧 Files skipped from review as they are similar to previous changes (16)
- src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleBasket.json
- src/main/webapp/i18n/en/sharing.json
- src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.spec.ts
- src/main/webapp/i18n/de/sharing.json
- src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java
- src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.spec.ts
- src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
- src/main/webapp/app/sharing/sharing.scss
- src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
- src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
- src/main/webapp/app/sharing/sharing.component.ts
- src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
- src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
🧰 Additional context used
📓 Path-based instructions (3)
`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:PascalC...
src/main/webapp/app/sharing/sharing.component.spec.ts
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...
src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
`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/**/*.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/sharing/sharing.component.html
🧠 Learnings (2)
📓 Common learnings
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10989
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java:122-135
Timestamp: 2025-06-06T14:47:54.300Z
Learning: In Artemis sharing platform integration (under "sharing" profile), endpoints in ExerciseSharingResource use checksum-based authentication with shared secrets instead of @PreAuthorize annotations. These endpoints (like getProblemStatement, getExerciseDetails, loadShoppingBasket) validate security through sharingInfo.checkChecksum() calls rather than role-based access control, as they serve as API interfaces for external sharing platform communication.
src/main/webapp/app/sharing/sharing.component.html (3)
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/i18n/de/student-dashboard.json:0-0
Timestamp: 2024-10-08T15:35:52.595Z
Learning: Avoid suggesting the phrase "Keine Aufgaben passen zu den gewählten Filtern" for the translation key `noExercisesFoundWithAppliedFilter` in the PR ls1intum/Artemis#8858.
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/i18n/de/student-dashboard.json:36-37
Timestamp: 2024-10-14T10:27:58.500Z
Learning: Avoid suggesting the translation change for "noExercisesFoundWithAppliedFilter" in the PR ls1intum/Artemis#8858. The preferred translation is "Keine Aufgaben passen zu den gewählten Filtern."
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/app/shared/sidebar/sidebar.component.html:18-22
Timestamp: 2024-07-09T10:08:14.161Z
Learning: Translation keys `artemisApp.courseOverview.general.noExercisesFoundWithAppliedFilter` and `artemisApp.courseOverview.general.noDataFound` should be added to the relevant translation files to ensure consistent translations across the application.
🧬 Code Graph Analysis (1)
src/main/webapp/app/sharing/sharing.component.spec.ts (4)
src/test/javascript/spec/helpers/mocks/service/mock-alert.service.ts (1)
MockAlertService
(3-8)src/test/javascript/spec/helpers/mocks/mock-router.ts (1)
MockRouter
(5-30)src/main/webapp/app/sharing/sharing.model.ts (1)
ShoppingBasket
(50-54)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)
🪛 HTMLHint (1.5.0)
src/main/webapp/app/sharing/sharing.component.html
[error] 1-1: Doctype must be declared before any non-comment content.
(doctype-first)
[error] 5-5: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 6-6: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 15-15: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 21-21: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 31-31: The attribute name of [ [(ngModel)] ] must be in lowercase.
(attr-lowercase)
[error] 50-50: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 50-50: The attribute name of [ [translateValues] ] must be in lowercase.
(attr-lowercase)
[error] 54-54: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 63-63: The attribute name of [ jhiSort ] must be in lowercase.
(attr-lowercase)
[error] 63-63: The attribute name of [ (sortChange) ] must be in lowercase.
(attr-lowercase)
[error] 66-66: The attribute name of [ jhiSortBy ] must be in lowercase.
(attr-lowercase)
[error] 66-66: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 67-67: The attribute name of [ jhiSortBy ] must be in lowercase.
(attr-lowercase)
[error] 67-67: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 82-82: The attribute name of [ ngModel ] must be in lowercase.
(attr-lowercase)
[error] 87-87: The attribute name of [ [ngStyle] ] must be in lowercase.
(attr-lowercase)
[error] 90-90: The attribute name of [ [routerLink] ] must be in lowercase.
(attr-lowercase)
[error] 93-93: The attribute name of [ [routerLink] ] must be in lowercase.
(attr-lowercase)
[error] 96-96: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 114-114: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
⏰ Context from checks skipped due to timeout of 90000ms (13)
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
- GitHub Check: client-style
- GitHub Check: client-tests
- GitHub Check: client-tests-selected
- GitHub Check: server-tests
- GitHub Check: server-style
- GitHub Check: Analyse
- GitHub Check: Mend Security Check
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
🔇 Additional comments (2)
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts (2)
692-700
: Inconsistent query-param naming (returnUrl
vsreturnURL
)Other parts of the codebase & tests use
returnURL
(capital “URL”).
Using a different casing here risks the component not picking up the value and breaking the import flow.Please align the key name across tests and production code.
706-709
: Return type ofloadDetailsForExercises
stub may be wrongThe stub returns a single
ProgrammingExercise
, but the real service likely returns an array or a wrapper DTO (judging from the method name).
A mismatch will let the test pass yet hide runtime type errors.Double-check the actual signature and adjust:
-const sharingServiceStub = jest.spyOn(programmingExerciseSharingService, 'loadDetailsForExercises'); -sharingServiceStub.mockReturnValue(of(programmingExercise)); +jest.spyOn(programmingExerciseSharingService, 'loadDetailsForExercises') + .mockReturnValue(of([programmingExercise])); // or appropriate DTO
restMockMvc.perform(post("/api/programming/sharing/setup-import").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(emptySetupInfo)) | ||
.accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().is5xxServerError()); | ||
|
||
} | ||
|
||
@Test | ||
@WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") | ||
void importExerciseInfosWrongChecksum() throws Exception { | ||
|
||
SharingInfoDTO sharingInfo = new SharingInfoDTO("Some Basket Token", TEST_RETURN_URL, SharingPlatformMockProvider.SHARING_BASEURL_PLUGIN, "Invalid Checksum", 0); | ||
|
||
restMockMvc.perform(post("/api/programming/sharing/import/basket/exercise-details").contentType(MediaType.APPLICATION_JSON) | ||
.content(objectMapper.writeValueAsString(sharingInfo)).accept(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Endpoint path drift – tests hit a non-existing route
The failing-checksum and missing-exercise tests use /api/programming/sharing/...
, while all production endpoints (and the rest of the tests) use /api/sharing/...
.
These tests will pass for the wrong reason (Spring returns 404 → 5xx/400) instead of exercising the real controller.
- restMockMvc.perform(post("/api/programming/sharing/setup-import")
+ restMockMvc.perform(post("/api/sharing/setup-import")
Apply the same correction to the two checksum tests below.
📝 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.
restMockMvc.perform(post("/api/programming/sharing/setup-import").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(emptySetupInfo)) | |
.accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().is5xxServerError()); | |
} | |
@Test | |
@WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") | |
void importExerciseInfosWrongChecksum() throws Exception { | |
SharingInfoDTO sharingInfo = new SharingInfoDTO("Some Basket Token", TEST_RETURN_URL, SharingPlatformMockProvider.SHARING_BASEURL_PLUGIN, "Invalid Checksum", 0); | |
restMockMvc.perform(post("/api/programming/sharing/import/basket/exercise-details").contentType(MediaType.APPLICATION_JSON) | |
.content(objectMapper.writeValueAsString(sharingInfo)).accept(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()); | |
} | |
restMockMvc.perform(post("/api/sharing/setup-import") | |
.contentType(MediaType.APPLICATION_JSON) | |
.content(objectMapper.writeValueAsString(emptySetupInfo))) | |
.andDo(print()) | |
.andExpect(status().is5xxServerError()); |
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
around lines 404 to 417, the test methods use the incorrect endpoint path
starting with /api/programming/sharing/ instead of the correct /api/sharing/.
Update the endpoint paths in these tests to match the production endpoints by
replacing /api/programming/sharing/ with /api/sharing/ to ensure the tests hit
the real controller and validate the intended logic.
<body> | ||
<div class="content"> | ||
<table style="border-spacing: 50px 0px; border-collapse: separate"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid embedding a second <body>
tag inside a component template
Angular inserts component HTML into the existing document body. Having another <body>
element results in invalid DOM and unpredictable styling. Simply drop the outer <body>
wrapper.
-<body>
+<div class="sharing-component">
...
-</body>
+</div>
Also applies to: 120-122
🧰 Tools
🪛 HTMLHint (1.5.0)
[error] 1-1: Doctype must be declared before any non-comment content.
(doctype-first)
🤖 Prompt for AI Agents
In src/main/webapp/app/sharing/sharing.component.html at lines 1 to 3 and also
lines 120 to 122, remove the outer <body> tags from the component template
because Angular inserts component HTML into the existing document body, and
having a nested <body> tag creates invalid DOM and styling issues. Keep only the
inner content inside the <div class="content"> and below, without wrapping it in
another <body> element.
<thead> | ||
<th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th> | ||
<th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th> | ||
<th></th> | ||
</thead> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
<thead>
must contain a <tr>
wrapper
HTML spec requires header cells to be children of a <tr>
element. Rendering engines will auto-fix, but Angular’s HTML validators (and some screen readers) will flag this as invalid.
-<thead>
- <th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th>
- <th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th>
- <th></th>
-</thead>
+<thead>
+ <tr>
+ <th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th>
+ <th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th>
+ <th></th>
+ </tr>
+</thead>
📝 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.
<thead> | |
<th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th> | |
<th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th> | |
<th></th> | |
</thead> | |
<thead> | |
<tr> | |
<th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th> | |
<th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th> | |
<th></th> | |
</tr> | |
</thead> |
🧰 Tools
🪛 HTMLHint (1.5.0)
[error] 5-5: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
[error] 6-6: The attribute name of [ jhiTranslate ] must be in lowercase.
(attr-lowercase)
🤖 Prompt for AI Agents
In src/main/webapp/app/sharing/sharing.component.html between lines 4 and 8, the
<thead> element directly contains <th> elements without a <tr> wrapper, which
violates HTML specifications. Wrap all the <th> elements inside a <tr> element
within the <thead> to ensure valid HTML structure and proper accessibility.
End-to-End (E2E) Test Results Summary |
d7e0690
to
5e2d7af
Compare
There was a problem hiding this 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
♻️ Duplicate comments (13)
src/main/webapp/app/sharing/sharing.component.html (2)
1-3
:⚠️ Potential issueRemove the nested
<body>
element – still violates DOM rules
The outer<body>
wrapper makes the template an invalid fragment (Angular inserts component HTML inside the existing document body). The same remark was raised previously but the tag is still present.-<body> +<div class="sharing-component"> ... -</body> +</div>🧰 Tools
🪛 HTMLHint (1.5.0)
[error] 1-1: Doctype must be declared before any non-comment content.
(doctype-first)
4-8
:⚠️ Potential issue
<thead>
must wrap its cells in a<tr>
– spec violation remains
Header cells are still direct children of<thead>
, breaking HTML validity and some accessibility tooling.-<thead> - <th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th> - <th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th> - <th></th> -</thead> +<thead> + <tr> + <th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th> + <th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th> + <th></th> + </tr> +</thead>src/main/webapp/app/sharing/sharing.component.spec.ts (1)
145-147
:⚠️ Potential issueUndeclared custom matcher
toBeBetween
still used
The earlier review flagged this. The matcher is not defined in the repo and tests will fail at runtime. Replace with standard assertions or add the matcher.-expect(fixture.componentInstance.getTokenExpiryDate()).toBeBetween(new Date(Date.now() - 1000), new Date(Date.now() + 1000)); +const expiry = fixture.componentInstance.getTokenExpiryDate().getTime(); +expect(expiry).toBeGreaterThanOrEqual(Date.now() - 1000); +expect(expiry).toBeLessThanOrEqual(Date.now() + 1000);src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts (1)
1282-1303
: Nested subscriptions introduce memory-leak risk
createProgrammingExerciseForImportFromSharing()
subscribes toqueryParams
and then nests another subscription toloadDetailsForExercises
.
Neither subscription is disposed, so every route change (incl. param updates) adds additional active subscriptions.At minimum, chain the calls with
switchMap
and tie the outer subscription todestroy$
:this.activatedRoute.queryParams.pipe( takeUntil(this.destroy$), switchMap((qp: Params) => { Object.assign(this.sharingInfo, { ... }); return this.programmingExerciseSharingService.loadDetailsForExercises(this.sharingInfo); }) ).subscribe({ next: (exerciseDetails) => { ... }, error: (err) => this.alertService.error(`Failed to load exercise details from the sharing platform: ${err}`), });This follows the same feedback given in previous reviews and avoids growth of unseen subscriptions.
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (3)
232-241
:parseParamsToMap
still lacks basic null / length validation
The previous review already covered this; the comment is still applicable.
273-273
: Typo: “unfortunatelly” → “unfortunately”
Same remark as in the earlier review.
270-334
: Monolithic test violates single-responsibility & readability
importExerciseCompleteProcess
is ~60 lines, mocks half a dozen services and verifies several behaviours. Consider splitting as suggested earlier.src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java (4)
246-258
: Server-side HTML escaping breaks Markdown formattingEscaping here mangles legitimate Markdown that will later be rendered client-side. The earlier feedback still applies: return the raw statement and leave sanitisation to the frontend Markdown renderer.
366-370
: Manual=
stripping – switch to URL-safe Base64 encoderUse
Base64.getUrlEncoder().withoutPadding()
instead of regex replacement; fewer bugs, more intent.- String tokenInB64 = Base64.getEncoder().encodeToString(token.getBytes()).replaceAll("=+$", ""); + String tokenInB64 = Base64.getUrlEncoder().withoutPadding().encodeToString(token.getBytes());
456-459
: Token validation too permissiveOnly character class check; length & path traversals still possible. Strengthen validation as suggested earlier.
400-415
:⚠️ Potential issue
createHMAC
returns empty hash and ignores null PSK – security risk
- If the pre-shared key is absent, an NPE will occur.
- Swallowing
NoSuchAlgorithmException
/InvalidKeyException
and returning an empty hash defeats integrity checks.- String psk = sharingConnectorService.getSharingApiKeyOrNull(); - SecretKeySpec secretKeySpec = new SecretKeySpec(psk.getBytes(StandardCharsets.UTF_8), algorithm); + String psk = sharingConnectorService.getSharingApiKeyOrNull(); + if (psk == null || psk.isBlank()) { + throw new IllegalStateException("Sharing API key is not configured"); + } + SecretKeySpec secretKeySpec = new SecretKeySpec(psk.getBytes(StandardCharsets.UTF_8), algorithm); ... - catch (NoSuchAlgorithmException | InvalidKeyException e) { - return Base64.getEncoder().encodeToString(new byte[] {}); + catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new SharingException("Failed to create HMAC for export token", e); }🧰 Tools
🪛 ast-grep (0.38.1)
[warning] 406-406: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Mac.getInstance(algorithm)
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html(use-of-aes-ecb-java)
[warning] 406-406: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Mac.getInstance(algorithm)
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA(desede-is-deprecated-java)
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java (2)
54-68
: Inline inner class violates SRP & hides error handling
NewFileInputStream
should live in a utility class and useFiles.deleteIfExists
, emitting a warning on failure. Same comment was provided previously.
155-160
: Null body on error response
badRequest().body(null)
sends a JSON null which confuses clients; prefer.build()
or a structured error DTO.
🧹 Nitpick comments (8)
src/main/webapp/app/sharing/sharing.component.html (1)
108-115
: Fallback text is untranslated and contains a typo
The button already references a translation key. Including hard-coded fallback text (“Import Programming Exercise from Sharing Plattform”) defeats i18n and “Plattform” is misspelled.- <span class="hidden-sm-down" jhiTranslate="artemisApp.programmingExercise.sharing.import">Import Programming Exercise from Sharing Plattform</span> + <span class="hidden-sm-down" jhiTranslate="artemisApp.programmingExercise.sharing.import"></span>src/main/webapp/app/sharing/sharing.component.spec.ts (1)
140-141
:toHaveBeenCalledOnce
is not part of standard Jest matchers
Unlessjest-extended
(or similar) is configured, this will throw. PrefertoHaveBeenCalledTimes(1)
.-expect(errorSpy).toHaveBeenCalledOnce(); +expect(errorSpy).toHaveBeenCalledTimes(1);Also applies to: 162-165
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts (3)
10-10
: Use application-wide path aliases instead of relative importsAll other service/model imports in this file use the
app/...
alias.
ProgrammingExerciseSharingService
is pulled in via a relative'../services/...
path whileSharingInfo
correctly usesapp/sharing/...
.Mixing import styles hampers refactoring and breaks when the file is moved. Stick to the alias that is configured in
tsconfig.json
.-import { ProgrammingExerciseSharingService } from '../services/programming-exercise-sharing.service'; +import { ProgrammingExerciseSharingService } from 'app/programming/manage/services/programming-exercise-sharing.service';Also applies to: 43-43
947-970
: Duplicated template-loading branchesThe two branches differ only in the service call. Consider extracting the common success/error handling into a helper, or use a ternary to pick the observable:
const src$ = this.isImportFromSharing ? this.programmingExerciseSharingService.loadProblemStatementForExercises(this.sharingInfo) : this.fileService.getTemplateFile(this.programmingExercise.programmingLanguage, this.programmingExercise.projectType); src$.pipe(takeUntil(this.destroy$)).subscribe({ ... });This keeps the method concise and avoids future divergence.
965-966
: Hard-coded English error message breaks i18n
'Failed to load problem statement from the sharing platform.'
bypasses the translation pipeline. Replace with a translate key and let the template handle localisation, e.g.:this.alertService.error('artemisApp.programmingExercise.sharing.problemStatementLoadFailed');docker/artemis/config/dev-local-vc-local-ci.env (1)
5-5
: Prefer=
delimiter for .env variablesThe dotenv-linter warnings are correct – using
:
instead of=
breaks standard.env
parsing in many tools (docker compose
, SpringDotenv
, Github Actions, etc.).
Replace the colon with an equals sign.-SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing +SPRING_PROFILES_ACTIVE=artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 5-5: [IncorrectDelimiter] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key has incorrect delimiter
[warning] 5-5: [KeyWithoutValue] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key should be with a value or have an equal sign
[warning] 5-5: [LowercaseKey] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key should be in uppercase
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java (1)
174-183
: Safer temp-file cleanup for cache eviction
File.delete()
silently ignores failures. UseFiles.deleteIfExists(path)
to handle cross-platform issues and log failures explicitly.- File f = (File) notification.getValue(); - if (f != null) { - boolean deleted = f.delete(); - if (!deleted) { - log.info("Cannot delete {}", f.getName()); - } - } + File f = (File) notification.getValue(); + if (f != null) { + try { + Files.deleteIfExists(f.toPath()); + } catch (IOException ex) { + log.warn("Temp file {} could not be deleted", f.getAbsolutePath(), ex); + } + }src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java (1)
106-116
: Bad-request response lacks error contextWhen checksum validation fails the client receives a bare 400. Consider returning a Problem JSON with a reason, or at least 401/403 to distinguish auth failure.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (16)
.github/workflows/build.yml
is excluded by!**/*.yml
docs/admin/setup/sharing/sharingButtonArtemis.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharingButtonSharing.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharing_health1.png
is excluded by!**/*.png
,!**/*.png
docs/admin/setup/sharing/sharing_health2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/artemis_import.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/artemis_import2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_export.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_metadata.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_metadata2.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_namespace.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_search.png
is excluded by!**/*.png
,!**/*.png
docs/user/sharing/sharing_success.png
is excluded by!**/*.png
,!**/*.png
src/main/resources/config/application-dev.yml
is excluded by!**/*.yml
src/main/resources/config/application-sharing.yml
is excluded by!**/*.yml
src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleExercise.zip
is excluded by!**/*.zip
,!**/*.zip
📒 Files selected for processing (61)
build.gradle
(2 hunks)docker/artemis/config/dev-local-vc-local-ci.env
(1 hunks)docs/admin/extension-services.rst
(1 hunks)docs/admin/setup/sharing.rst
(1 hunks)docs/index.rst
(1 hunks)docs/user/sharing.rst
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/Constants.java
(2 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/RestTemplateConfiguration.java
(2 hunks)src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/dto/SharingInfoDTO.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java
(2 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
(4 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ProgrammingExerciseImportFromSharingService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
(1 hunks)src/main/webapp/app/app.routes.ts
(1 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.html
(1 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts
(7 hunks)src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
(1 hunks)src/main/webapp/app/programming/manage/exercise/programming-exercise.component.ts
(1 hunks)src/main/webapp/app/programming/manage/programming-exercise-management.route.ts
(1 hunks)src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.spec.ts
(1 hunks)src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
(1 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-creation-config.ts
(1 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-update.component.html
(3 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
(5 hunks)src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
(17 hunks)src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.spec.ts
(1 hunks)src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/search-result-dto.model.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.html
(1 hunks)src/main/webapp/app/sharing/sharing.component.spec.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.ts
(1 hunks)src/main/webapp/app/sharing/sharing.model.ts
(1 hunks)src/main/webapp/app/sharing/sharing.route.ts
(1 hunks)src/main/webapp/app/sharing/sharing.scss
(1 hunks)src/main/webapp/i18n/de/health.json
(1 hunks)src/main/webapp/i18n/de/programmingExercise.json
(1 hunks)src/main/webapp/i18n/de/sharing.json
(1 hunks)src/main/webapp/i18n/en/health.json
(1 hunks)src/main/webapp/i18n/en/programmingExercise.json
(1 hunks)src/main/webapp/i18n/en/sharing.json
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationIndependentTest.java
(2 hunks)src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java
(3 hunks)src/test/javascript/spec/helpers/mocks/programming-exercise-creation-config-mock.ts
(1 hunks)src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleBasket.json
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- src/main/webapp/i18n/de/health.json
- src/main/webapp/i18n/en/health.json
- docs/admin/setup/sharing.rst
🚧 Files skipped from review as they are similar to previous changes (51)
- src/main/webapp/app/programming/manage/exercise/programming-exercise.component.ts
- docs/admin/extension-services.rst
- build.gradle
- docs/index.rst
- src/main/webapp/app/programming/manage/update/programming-exercise-update.component.html
- src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
- src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java
- src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.html
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingException.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorServiceTest.java
- src/main/webapp/app/programming/manage/update/programming-exercise-creation-config.ts
- src/main/webapp/i18n/en/sharing.json
- src/main/webapp/app/sharing/sharing.scss
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorHealthCheckRegistryTest.java
- src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.spec.ts
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSetupInfo.java
- src/main/webapp/app/sharing/sharing.route.ts
- src/main/webapp/i18n/de/sharing.json
- src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseRepository.java
- src/test/resources/de/tum/cit/aet/artemis/programming/service/sharing/basket/sampleBasket.json
- src/main/webapp/app/programming/manage/programming-exercise-management.route.ts
- src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java
- src/main/java/de/tum/cit/aet/artemis/core/dto/SharingInfoDTO.java
- src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
- src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts
- src/main/webapp/app/app.routes.ts
- src/main/webapp/i18n/de/programmingExercise.json
- src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationIndependentTest.java
- src/main/java/de/tum/cit/aet/artemis/core/config/RestTemplateConfiguration.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingServiceTest.java
- src/main/webapp/i18n/en/programmingExercise.json
- src/test/javascript/spec/helpers/mocks/programming-exercise-creation-config-mock.ts
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingSupportResourceTest.java
- src/main/java/de/tum/cit/aet/artemis/core/config/Constants.java
- src/main/webapp/app/programming/manage/update/programming-exercise-update.component.spec.ts
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ProgrammingExerciseImportFromSharingService.java
- src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingMultipartZipFile.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingHealthIndicator.java
- src/main/webapp/app/sharing/search-result-dto.model.ts
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingInfoDTOTest.java
- src/main/webapp/app/sharing/sharing.model.ts
- src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.spec.ts
- src/main/webapp/app/programming/shared/actions/programming-exercise-instructor-exercise-sharing.component.ts
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceExportTest.java
- src/main/webapp/app/sharing/sharing.component.ts
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
- docs/user/sharing.rst
- src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.ts
🧰 Additional context used
📓 Path-based instructions (4)
`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:PascalC...
src/main/webapp/app/sharing/sharing.component.spec.ts
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...
src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
`src/main/java/**/*.java`: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,de...
src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
`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/**/*.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/sharing/sharing.component.html
🧬 Code Graph Analysis (1)
src/main/webapp/app/sharing/sharing.component.spec.ts (4)
src/test/javascript/spec/helpers/mocks/service/mock-alert.service.ts (1)
MockAlertService
(3-8)src/test/javascript/spec/helpers/mocks/mock-router.ts (1)
MockRouter
(5-30)src/main/webapp/app/sharing/sharing.model.ts (1)
ShoppingBasket
(50-54)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)
🪛 ast-grep (0.38.1)
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
[warning] 406-406: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Mac.getInstance(algorithm)
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html
(use-of-aes-ecb-java)
[warning] 406-406: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Mac.getInstance(algorithm)
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA
(desede-is-deprecated-java)
🪛 dotenv-linter (3.3.0)
docker/artemis/config/dev-local-vc-local-ci.env
[warning] 5-5: [IncorrectDelimiter] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key has incorrect delimiter
[warning] 5-5: [KeyWithoutValue] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key should be with a value or have an equal sign
[warning] 5-5: [LowercaseKey] The SPRING_PROFILES_ACTIVE: artemis,scheduling,localci,localvc,buildagent,core,dev,docker,sharing key should be in uppercase
🪛 HTMLHint (1.5.0)
src/main/webapp/app/sharing/sharing.component.html
[error] 1-1: Doctype must be declared before any non-comment content.
(doctype-first)
⏰ Context from checks skipped due to timeout of 90000ms (13)
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: client-tests-selected
- GitHub Check: client-style
- GitHub Check: client-tests
- GitHub Check: server-tests
- GitHub Check: server-style
- GitHub Check: Analyse
- GitHub Check: Mend Security Check
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
🔇 Additional comments (1)
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts (1)
722-727
: Potential mismatch between build-plan serialization and import flags
buildPlanConfiguration
is serialised when any of
customizeBuildPlanWithAeolus || isImportFromFile || isImportFromSharing
is true.
For an import-from-sharing scenario that does not customise the build plan the Windfile is nevertheless serialised although the backend will recreate the plan on its own (setUpFromSharingImport
). Double-check that this does not overwrite configuration coming from the sharing service.
import { TranslateDirective } from 'app/shared/language/translate.directive'; | ||
import { MockTranslateService, TranslatePipeMock } from 'test/helpers/mocks/service/mock-translate.service'; | ||
import { ProfileService } from 'app/core/layouts/profiles/shared/profile.service'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Import path likely unresolved – verify TypeScript path mapping
'test/helpers/…'
assumes a path alias. Unless tsconfig
contains "paths": { "test/*": ["src/test/javascript/spec/*"] }
, the module won’t be found during compilation.
🏁 Script executed:
#!/bin/bash
# Verify that the import actually resolves
fd mock-translate.service | head
Length of output: 107
🏁 Script executed:
#!/bin/bash
# Locate and inspect tsconfig files for path mappings
rg -n '"paths":' -n tsconfig*.json
rg -n '"test/*":' -n tsconfig*.json
Length of output: 222
Missing TypeScript test/*
path mapping
The import
import { MockTranslateService, TranslatePipeMock } from 'test/helpers/mocks/service/mock-translate.service';
resolves to
src/test/javascript/spec/helpers/mocks/service/mock-translate.service.ts
but neither tsconfig.json nor tsconfig.spec.json defines a "test/*"
alias under compilerOptions.paths
. Without that mapping, the module won’t compile.
Please update your tsconfig to include:
// in tsconfig.json (or extend in tsconfig.spec.json if it’s test-only)
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"test/*": ["src/test/javascript/spec/*"]
}
}
}
Alternatively, convert the import to a relative path:
import { MockTranslateService, TranslatePipeMock }
from '../../../test/javascript/spec/helpers/mocks/service/mock-translate.service';
🤖 Prompt for AI Agents
In src/main/webapp/app/sharing/sharing.component.spec.ts at lines 1 to 3, the
import path 'test/helpers/mocks/service/mock-translate.service' is unresolved
because the TypeScript configuration lacks a "test/*" path mapping. Fix this by
adding the "test/*" alias to the "paths" section in tsconfig.json or
tsconfig.spec.json, mapping it to "src/test/javascript/spec/*". Alternatively,
replace the import with a correct relative path to the mock-translate.service
file.
if (this.sharingInfo) { | ||
isEditFieldDisplayedMapping[ProgrammingExerciseInputField.SHORT_NAME] = true; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SHORT_NAME
field forced visible for every scenario
isEditFieldDisplayedRecord
now checks if (this.sharingInfo)
– but sharingInfo
is always instantiated (new SharingInfo()
), so the condition is perpetually true and the SHORT_NAME input is shown even when the user is not importing from the sharing platform.
Use the dedicated flag that already exists:
- if (this.sharingInfo) {
+ if (this.isImportFromSharing) {
isEditFieldDisplayedMapping[ProgrammingExerciseInputField.SHORT_NAME] = true;
}
📝 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.
if (this.sharingInfo) { | |
isEditFieldDisplayedMapping[ProgrammingExerciseInputField.SHORT_NAME] = true; | |
} | |
// Only show the SHORT_NAME field when importing from the sharing platform | |
if (this.isImportFromSharing) { | |
isEditFieldDisplayedMapping[ProgrammingExerciseInputField.SHORT_NAME] = true; | |
} |
🤖 Prompt for AI Agents
In
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
around lines 132 to 135, the condition checking this.sharingInfo is always true
because sharingInfo is always instantiated, causing the SHORT_NAME field to be
shown incorrectly. Replace the condition with the dedicated flag that indicates
whether the user is importing from the sharing platform to control the
visibility of the SHORT_NAME input correctly.
this.subscribeToSaveResponse(this.programmingExerciseService.importFromFile(this.programmingExercise, this.courseId)); | ||
} else if (this.isImportFromSharing) { | ||
this.courseService.find(this.courseId).subscribe((res) => { | ||
this.programmingExerciseSharingService.setUpFromSharingImport(this.programmingExercise, res.body!, this.sharingInfo).subscribe({ | ||
next: (response: HttpResponse<ProgrammingExercise>) => { | ||
this.alertService.success('artemisApp.programmingExercise.created', { param: this.programmingExercise.title }); | ||
this.onSaveSuccess(response.body!); | ||
}, | ||
error: (err) => this.onSaveError(err), | ||
}); | ||
}); | ||
} else if (this.isImportFromExistingExercise) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
No error handling for failed course lookup before sharing import
If courseService.find(this.courseId)
fails (e.g. invalid courseId
or network error) the component silently stalls with isSaving = true
. Add error handling to surface the problem and unblock the UI.
- } else if (this.isImportFromSharing) {
- this.courseService.find(this.courseId).subscribe((res) => {
+ } else if (this.isImportFromSharing) {
+ this.courseService.find(this.courseId).subscribe({
+ next: (res) => {
this.programmingExerciseSharingService.setUpFromSharingImport(this.programmingExercise, res.body!, this.sharingInfo).subscribe({
next: ...
error: (err) => this.onSaveError(err),
});
- });
+ },
+ error: (err) => this.onSaveError(err),
+ });
📝 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.
this.subscribeToSaveResponse(this.programmingExerciseService.importFromFile(this.programmingExercise, this.courseId)); | |
} else if (this.isImportFromSharing) { | |
this.courseService.find(this.courseId).subscribe((res) => { | |
this.programmingExerciseSharingService.setUpFromSharingImport(this.programmingExercise, res.body!, this.sharingInfo).subscribe({ | |
next: (response: HttpResponse<ProgrammingExercise>) => { | |
this.alertService.success('artemisApp.programmingExercise.created', { param: this.programmingExercise.title }); | |
this.onSaveSuccess(response.body!); | |
}, | |
error: (err) => this.onSaveError(err), | |
}); | |
}); | |
} else if (this.isImportFromExistingExercise) { | |
this.subscribeToSaveResponse(this.programmingExerciseService.importFromFile(this.programmingExercise, this.courseId)); | |
} else if (this.isImportFromSharing) { | |
this.courseService.find(this.courseId).subscribe({ | |
next: (res) => { | |
this.programmingExerciseSharingService.setUpFromSharingImport(this.programmingExercise, res.body!, this.sharingInfo).subscribe({ | |
next: (response: HttpResponse<ProgrammingExercise>) => { | |
this.alertService.success('artemisApp.programmingExercise.created', { param: this.programmingExercise.title }); | |
this.onSaveSuccess(response.body!); | |
}, | |
error: (err) => this.onSaveError(err), | |
}); | |
}, | |
error: (err) => this.onSaveError(err), | |
}); | |
} else if (this.isImportFromExistingExercise) { |
🤖 Prompt for AI Agents
In
src/main/webapp/app/programming/manage/update/programming-exercise-update.component.ts
around lines 770 to 781, the call to courseService.find(this.courseId) lacks
error handling, causing the UI to stall with isSaving = true if the course
lookup fails. Add an error callback to the subscribe method that calls
this.onSaveError with the error to handle failures properly and unblock the UI.
@PostMapping(SHARINGEXPORT_RESOURCE_PATH + "/{exerciseId}") | ||
public ResponseEntity<String> exportExerciseToSharing(@RequestBody String callBackUrl, @PathVariable("exerciseId") Long exerciseId) { | ||
try { | ||
URI uriRedirect = exerciseSharingService.exportExerciseToSharing(exerciseId).toURI(); | ||
uriRedirect = UriBuilder.fromUri(uriRedirect).queryParam("callBack", callBackUrl).build(); | ||
return ResponseEntity.ok().body("\"" + uriRedirect.toString() + "\""); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Manual JSON string building – return structured DTO
Wrapping the URL in quotes creates invalid JSON unless double-encoded by consumer. Return a DTO or Map.of("url", uriRedirect.toString())
and let Spring serialise it.
- return ResponseEntity.ok().body("\"" + uriRedirect.toString() + "\"");
+ return ResponseEntity.ok(Map.of("sharingUrl", uriRedirect.toString()));
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java
around lines 172 to 178, the method manually builds a JSON string by wrapping
the URL in quotes, which results in invalid JSON unless double-encoded by the
consumer. To fix this, change the return type to return a structured DTO or use
Map.of("url", uriRedirect.toString()) so that Spring can automatically serialize
it into valid JSON.
End-to-End (E2E) Test Results Summary |
End-to-End (E2E) Test Results Summary
|
There was a problem hiding this 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
♻️ Duplicate comments (4)
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (4)
273-273
: Typo in comment
unfortunatelly
→unfortunately
232-241
:⚠️ Potential issueAdd missing null‐checks in helper to avoid NPE
parseParamsToMap
still trusts the caller completely. Ifparams
isnull
or containsnull
elements you hit an NPE before the AssertJ check fires.
The validation proposed in the previous review is still applicable.private Map<String, String> parseParamsToMap(String... params) { - Map<String, String> paramsMap = new HashMap<>(); - assertThat(params.length % 2).isEqualTo(0).describedAs("paramList must contain even elements"); + Objects.requireNonNull(params, "params cannot be null"); + assertThat(params.length % 2).isEqualTo(0).describedAs("paramList must contain an even number of elements"); + Map<String, String> paramsMap = new HashMap<>(); for (int i = 0; i < params.length; i = i + 2) { String paramName = params[i]; String paramValue = params[i + 1]; + if (paramName == null || paramValue == null) { + throw new IllegalArgumentException("Parameter names and values cannot be null"); + } paramsMap.put(paramName, paramValue); } return paramsMap; }
265-267
:⚠️ Potential issueEndpoint path still incorrect – tests call a non-existing route
Earlier feedback pointed out that the correct production route is
/api/sharing/*
, not/api/programming/sharing/*
.
The three occurrences below are still wrong and therefore do not test the real controller:•
importBasketWrongChecksum
– lines 265-267
•importExerciseCompleteProcess
– lines 326-327
•setUpWithMissingExercise
– lines 404-405-"/api/programming/sharing/ +"/api/sharing/Also applies to: 326-327, 404-405
270-334
: 🛠️ Refactor suggestionMonolithic test violates SRP – refactor into focused tests
importExerciseCompleteProcess
spans ~60 lines and covers basket import, detail retrieval, problem-statement validation and setup-import. Splitting this into 3-4 small tests (e.g.shouldImportBasket
,shouldRetrieveExerciseDetails
,shouldRetrieveProblemStatement
,shouldCompleteSetupImport
) improves readability and failure diagnostics.
🧹 Nitpick comments (1)
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (1)
172-173
: Remove unused variable
sampleBasket
is read but never used intestImportBasketFail
, which may hide accidental leftovers and adds I/O overhead.-String sampleBasket = IOUtils.toString(...);
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...
src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
🧠 Learnings (1)
📓 Common learnings
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10989
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java:122-135
Timestamp: 2025-06-06T14:47:54.300Z
Learning: In Artemis sharing platform integration (under "sharing" profile), endpoints in ExerciseSharingResource use checksum-based authentication with shared secrets instead of @PreAuthorize annotations. These endpoints (like getProblemStatement, getExerciseDetails, loadShoppingBasket) validate security through sharingInfo.checkChecksum() calls rather than role-based access control, as they serve as API interfaces for external sharing platform communication.
🧬 Code Graph Analysis (1)
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (4)
src/main/webapp/app/sharing/sharing.model.ts (1)
ShoppingBasket
(50-54)src/main/webapp/app/core/course/shared/entities/course.model.ts (1)
Course
(59-180)src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java (1)
Repository
(18-95)src/main/webapp/app/core/layouts/profiles/profile-info.model.ts (1)
ProgrammingLanguageFeature
(115-124)
⏰ Context from checks skipped due to timeout of 90000ms (12)
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
- GitHub Check: client-tests-selected
- GitHub Check: client-style
- GitHub Check: client-tests
- GitHub Check: Analyse
- GitHub Check: server-style
- GitHub Check: server-tests
- GitHub Check: Build .war artifact
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
End-to-End (E2E) Test Results Summary |
1 similar comment
End-to-End (E2E) Test Results Summary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (4)
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (4)
232-241
: Input validation still missing – risk of NPE & assertion failuresThe helper does not guard against
null
or an odd number of arguments. This is identical to the issue raised in a previous review.
265-267
: Wrong endpoint prefix (/api/programming/sharing/...
) – tests target a non-existing routeThe production controller is exposed under
/api/sharing/**
. Using the outdated prefix lets the test pass for the wrong reason (Spring returns 404 → 400/500).
404-405
: Same endpoint drift here – adjust to/api/sharing/setup-import
270-334
: 93-line test violates “small & specific” guideline – split into focused tests
importExerciseCompleteProcess
mixes basket import, problem-statement retrieval, exercise-details parsing and final setup. Break it up into four atomic tests as already suggested to improve readability and maintenance.
🧹 Nitpick comments (1)
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java (1)
91-100
: Clean up provisional test and align with test guidelines
- Method name should convey the behaviour under test (e.g.
shouldReturnEmptyListWhenCourseDoesNotExist
).- Add an explicit
@WithMockUser
to satisfy RBAC expectations of the coding guidelines.- Remove emoticon & PR URL from Javadoc, replace with a TODO that points to the tracked issue so the test can be located and deleted later.
Suggested change:
- /** - * This method is solely added to circumvent problem from open pull request - * https://github.com/ls1intum/Artemis/pull/10997 :-(. - * It can be deleted after resolving this issue - */ - @Test - void findAllByCourseId() { - assertThat(programmingExerciseService.findAllByCourseId(9999L)).describedAs("course with id 9999 should not exist").isEmpty(); - } + /** + * TODO Remove once issue #10997 is fixed – ensures the workaround service + * method returns an empty list for a non-existing course. + */ + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void shouldReturnEmptyListWhenCourseDoesNotExist() { + assertThat(programmingExerciseService.findAllByCourseId(9999L)) + .describedAs("Service should return empty list for a non-existing course id") + .isEmpty(); + }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...
src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
`src/main/java/**/*.java`: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,de...
src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java
🧠 Learnings (1)
📓 Common learnings
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10989
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java:122-135
Timestamp: 2025-06-06T14:47:54.300Z
Learning: In Artemis sharing platform integration (under "sharing" profile), endpoints in ExerciseSharingResource use checksum-based authentication with shared secrets instead of @PreAuthorize annotations. These endpoints (like getProblemStatement, getExerciseDetails, loadShoppingBasket) validate security through sharingInfo.checkChecksum() calls rather than role-based access control, as they serve as API interfaces for external sharing platform communication.
⏰ Context from checks skipped due to timeout of 90000ms (13)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
- GitHub Check: client-style
- GitHub Check: client-tests
- GitHub Check: client-tests-selected
- GitHub Check: server-style
- GitHub Check: server-tests
- GitHub Check: Analyse
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
🔇 Additional comments (1)
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (1)
243-246
:sharingApiKey
may benull
– checksum calculation can blow up
@Value("${artemis.sharing.apikey:#{null}}")
allows anull
to be injected. If that happens,SecretChecksumCalculator.calculateChecksum
is very likely to throw or silently produce a wrong hash, breaking every test that relies on it.
Add a fail-fast guard or provide a default key via test properties.
/** | ||
* Not for public use! | ||
* This method is solely added to circumvent a problem from open pull request | ||
* https://github.com/ls1intum/Artemis/pull/10997 :-(. | ||
* | ||
* <pre> | ||
* method de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository.findAllByCourseId(java.lang.Long) is not used in production code | ||
* </pre> | ||
* | ||
* It can be deleted after resolving the related issue | ||
* | ||
* @param id the course id | ||
*/ | ||
public List<ProgrammingExercise> findAllByCourseId(Long id) { | ||
return programmingExerciseRepository.findAllByCourseId(id); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Leaking non-public helper as a public
API – downgrade visibility & mark for removal
The method is intentionally a stop-gap for test compilation, yet making it public
silently broadens the service’s contract and invites unintended production use.
Recommend:
- Reduce visibility to package-private and/or add
@VisibleForTesting
(or at least@Deprecated
) so IDEs warn callers. - Rename the parameter to
courseId
for clarity. - Add
@Transactional(readOnly = true)
or@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
if you keep it, to align with other read-only accessors. - Replace the emoticon and PR URL in the Javadoc with a TODO referencing the issue tracker to keep the codebase professional.
Example patch:
- public List<ProgrammingExercise> findAllByCourseId(Long id) {
- return programmingExerciseRepository.findAllByCourseId(id);
+ /**
+ * TEMPORARY workaround for <issue-id>. Remove once resolved.
+ */
+ @VisibleForTesting
+ /* package */ List<ProgrammingExercise> findAllByCourseId(Long courseId) {
+ return programmingExerciseRepository.findAllByCourseId(courseId);
+ }
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java
around lines 481 to 496, the method findAllByCourseId is unnecessarily public
and lacks proper annotations and documentation. Change its visibility to
package-private, rename the parameter from id to courseId for clarity, add
@VisibleForTesting or at least @Deprecated annotation to signal limited use, and
annotate the method with @Transactional(readOnly = true) or
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) to match
other read-only methods. Also, update the Javadoc to remove the emoticon and PR
URL, replacing them with a professional TODO referencing the issue tracker.
End-to-End (E2E) Test Results Summary |
62e2d26
to
6cb84af
Compare
End-to-End (E2E) Test Results Summary
|
There was a problem hiding this 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
♻️ Duplicate comments (10)
src/main/webapp/app/sharing/sharing.component.html (1)
4-8
: 🛠️ Refactor suggestionWrap
<th>
elements inside a<tr>
within<thead>
HTML requires<thead>
to contain one or more<tr>
rows, not direct<th>
children. Encapsulate the header cells in a<tr>
.- <thead> - <th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th> - <th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th> - <th></th> - </thead> + <thead> + <tr> + <th jhiTranslate="artemisApp.sharing.importedExercise">Imported Exercise</th> + <th jhiTranslate="artemisApp.sharing.courseToImport">Course to import</th> + <th></th> + </tr> + </thead>src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java (6)
257-257
: Remove server-side HTML escaping of Markdown
See earlier review – escaping breaks Markdown rendering.
368-369
: Stop stripping Base64 padding; use URL-encoder instead
402-405
:psk
can be null – add a defensive check
413-415
: Don’t return predictable value on crypto error
427-430
: Avoid manual “space→+” sanitisation – fix URL decoding at controller level
456-459
: Strengthen token validation (length, path separators, …)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java (3)
232-241
: Add null / even-length validation toparseParamsToMap
265-267
: Tests hit non-existing route/api/programming/sharing/**
Endpoints use
/api/sharing/**
. Update paths.
270-334
:importExerciseCompleteProcess
is 90+ lines – split into focussed tests
🧹 Nitpick comments (1)
src/main/webapp/app/sharing/sharing.component.html (1)
3-3
: Avoid inline styles for layout
Consider moving hard-codedborder-spacing
andborder-collapse
into a CSS class (e.g.,.sharing-table
) to improve maintainability and theming.-<table style="border-spacing: 50px 0px; border-collapse: separate"> +<table class="sharing-table">
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java
(2 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
(1 hunks)src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
(1 hunks)src/main/webapp/app/sharing/sharing.component.html
(1 hunks)src/main/webapp/i18n/de/health.json
(1 hunks)src/main/webapp/i18n/en/health.json
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- src/main/webapp/i18n/de/health.json
- src/main/webapp/i18n/en/health.json
- src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java
- src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java
- src/main/java/de/tum/cit/aet/artemis/core/service/ProfileService.java
- src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingPlatformMockProvider.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java
- src/main/webapp/app/programming/manage/services/programming-exercise-sharing.service.ts
- src/main/java/de/tum/cit/aet/artemis/core/web/SharingSupportResource.java
- src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/SharingConnectorService.java
🧰 Additional context used
📓 Path-based instructions (3)
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...
src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java
`src/main/java/**/*.java`: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,de...
src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
`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/**/*.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/sharing/sharing.component.html
🧠 Learnings (2)
📓 Common learnings
Learnt from: Wallenstein61
PR: ls1intum/Artemis#10989
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java:122-135
Timestamp: 2025-06-06T14:47:54.300Z
Learning: In Artemis sharing platform integration (under "sharing" profile), endpoints in ExerciseSharingResource use checksum-based authentication with shared secrets instead of @PreAuthorize annotations. These endpoints (like getProblemStatement, getExerciseDetails, loadShoppingBasket) validate security through sharingInfo.checkChecksum() calls rather than role-based access control, as they serve as API interfaces for external sharing platform communication.
src/main/webapp/app/sharing/sharing.component.html (3)
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/i18n/de/student-dashboard.json:0-0
Timestamp: 2024-10-08T15:35:52.595Z
Learning: Avoid suggesting the phrase "Keine Aufgaben passen zu den gewählten Filtern" for the translation key `noExercisesFoundWithAppliedFilter` in the PR ls1intum/Artemis#8858.
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/i18n/de/student-dashboard.json:36-37
Timestamp: 2024-10-14T10:27:58.500Z
Learning: Avoid suggesting the translation change for "noExercisesFoundWithAppliedFilter" in the PR ls1intum/Artemis#8858. The preferred translation is "Keine Aufgaben passen zu den gewählten Filtern."
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/app/shared/sidebar/sidebar.component.html:18-22
Timestamp: 2024-07-09T10:08:14.161Z
Learning: Translation keys `artemisApp.courseOverview.general.noExercisesFoundWithAppliedFilter` and `artemisApp.courseOverview.general.noDataFound` should be added to the relevant translation files to ensure consistent translations across the application.
🪛 ast-grep (0.38.1)
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
[warning] 406-406: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Mac.getInstance(algorithm)
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html
(use-of-aes-ecb-java)
[warning] 406-406: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Mac.getInstance(algorithm)
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA
(desede-is-deprecated-java)
🪛 HTMLHint (1.5.0)
src/main/webapp/app/sharing/sharing.component.html
[error] 1-1: Doctype must be declared before any non-comment content.
(doctype-first)
⏰ Context from checks skipped due to timeout of 90000ms (14)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Build .war artifact
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: client-tests
- GitHub Check: client-style
- GitHub Check: client-tests-selected
- GitHub Check: server-tests
- GitHub Check: server-style
- GitHub Check: Analyse
- GitHub Check: Mend Security Check
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build and Push Docker Image / Build Docker Image for ls1intum/artemis
- GitHub Check: Build .war artifact
🔇 Additional comments (4)
src/main/webapp/app/sharing/sharing.component.html (4)
15-15
: Verify translation key for “No importable exercises”
Ensure thatartemisApp.sharing.error.noImportedExercises
exists in youri18n/en/sharing.json
andi18n/de/sharing.json
under theerror
namespace and matches the intended message.
14-44
: Exercise list: correct use of new Angular template syntax and accessibility
The@if
and@for
constructs adhere to the updated Angular syntax guidelines, and the radio inputs includearia-label
andtitle
attributes for screen readers. Great job on this integration!
62-102
: Course list: correct sorting and selection implementation
ThejhiSort
directives on<tr>
headers, the use of@for
for iteration, and the labeled radio inputs for course selection follow best practices. All looks solid here.
107-116
: Import button implementation looks correct
The disabled state is tied to the model, theid
is unique, and the click handler directs to the import flow with appropriate iconography and translation key usage.
<body> | ||
<div class="content"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove <body>
wrapper from component template
Angular component templates should not include their own <body>
tags; this leads to invalid DOM nesting. Replace the outer <body>
/</body>
with a wrapping <div>
(e.g., <div class="sharing-component">
).
-<body>
+<div class="sharing-component">
...
-</body>
+</div>
Also applies to: 121-122
🧰 Tools
🪛 HTMLHint (1.5.0)
[error] 1-1: Doctype must be declared before any non-comment content.
(doctype-first)
🤖 Prompt for AI Agents
In src/main/webapp/app/sharing/sharing.component.html at lines 1-2 and 121-122,
remove the <body> and </body> tags from the component template as Angular
components should not include their own body tags. Replace these with a wrapping
<div> element, such as <div class="sharing-component">, to ensure valid DOM
structure and proper encapsulation.
String exercisesZipUrl = correctLocalHostInDocker(sharingInfo.apiBaseURL()) + "/basket/{basketToken}/repository/" + itemPosition + "?format={format}"; | ||
Resource zipInputResource = restTemplate.getForObject(exercisesZipUrl, Resource.class, | ||
Map.of("basketToken", sharingInfo.basketToken(), "format", "artemis")); | ||
if (zipInputResource == null) { | ||
throw new SharingException("Could not retrieve basket item resource"); | ||
} | ||
InputStream zipInput = zipInputResource.getInputStream(); | ||
|
||
File basketFile = Files.createTempFile("basketStore", ".zip").toFile(); | ||
FileCopyUtils.copy(zipInput, new FileOutputStream(basketFile)); | ||
return basketFile; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Close streams in CacheLoader.load
to avoid FD leaks
zipInput
and the FileOutputStream
are not closed, leaking file descriptors. Wrap both in try-with-resources.
-InputStream zipInput = zipInputResource.getInputStream();
-
-File basketFile = Files.createTempFile("basketStore", ".zip").toFile();
-FileCopyUtils.copy(zipInput, new FileOutputStream(basketFile));
-
-return basketFile;
+try (InputStream zipInput = zipInputResource.getInputStream();
+ FileOutputStream fos = new FileOutputStream(Files.createTempFile("basketStore", ".zip").toFile())) {
+ FileCopyUtils.copy(zipInput, fos);
+ return new File(fos.getFD().sync(); // ensure data is flushed
+}
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingService.java
around lines 190 to 201, the InputStream zipInput and the FileOutputStream used
to copy data to basketFile are not closed, causing file descriptor leaks. Fix
this by wrapping both the InputStream and the FileOutputStream in
try-with-resources blocks to ensure they are automatically closed after use.
End-to-End (E2E) Test Results Summary |
This pull resolves #9909
Checklist
General
Server
Client
authorities
to all new routes and checked the course groups for displaying navigation elements (links, buttons).Changes affecting Programming Exercises
Motivation and Context
The CodeAbility Project currently operates and maintains a platform for sharing (among other content) programming exercises between interested parties. It is open to the programming teaching community.
CodeAbility also operates an Artemis instance that serves its 6 Austrian partners as a platform for programming teaching.
Artemis is currently a major source of programming exercises, as well as a potential target for re-using programming exercises.
However the exchange between separated instances of Artemis is only supported by a manual exchange of zipped exercises files.
We have implemented a REST-based connector interface in the sharing platform to import and export exercises between interested applications.
This connector interface allows among other features the import and export of programming exercises in an appropriate format.
We have already implemented an prototype integration of Artemis with the sharing platform based on the connector interface in order to allow for the smooth sharing of exercises.
Description
An additional spring profile "sharing" is implemented, that enables the functionality to export/import programming exercises via the sharing plattform.
The sharing requires an registration of the artemis instance at [email protected]. During the registration a pre shared key is exchanged, that allows the communication between artemis and the sharing plattform.
When the sharing profile is enabled, (and configuration is initialized properly), the the sharing platform connects regularly with the artemis instance to query the connector configuration.
Additional the artemis instance can trigger an configuration request for the sharing plattform at application startup.
Details can be found at https://sharing-codeability.uibk.ac.at/development/sharing/codeability-sharing-platform/-/wikis/Artemis-Connection/Connector-Interface .
A preliminary setup guide can be found at https://development.pages.sharing-codeability.uibk.ac.at/artemis/artemis/admin/setup/sharing.html .
Steps for Testing
Prerequisites:
Follow the steps at https://development.pages.sharing-codeability.uibk.ac.at/artemis/artemis/user/sharing.html for export to Artemis and the import back to artemis.
Exam Mode Testing
This feature is not relevant for exam mode testing. (Just exercise imports)
Testserver States
TODO: In progress.
You can manage test servers using Helios. Check environment statuses in the environment list. To deploy to a test server, go to the CI/CD page, find your PR or branch, and trigger the deployment.
Review Progress
Performance Review
Preliminary Remark: Import and Export of programming exercises does not influence any performance with student features.
Code Review
Manual Tests
Exam Mode Test
Performance Tests
Test Coverage
Screenshots
Summary by CodeRabbit
New Features
Documentation
Style
Tests
Chores