diff --git a/.gitignore b/.gitignore index 202eea35..999c709a 100644 --- a/.gitignore +++ b/.gitignore @@ -166,4 +166,6 @@ cython_debug/ node_modules/ -application-local.yml \ No newline at end of file +application-local.yml + +server/application-server/postgres-data/ \ No newline at end of file diff --git a/package.json b/package.json index 0239ce68..a543c2ae 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ ], "scripts": { "generate:api:clean": "rimraf webapp/src/app/core/modules/openapi", - "generate:api:application-server-specs": "cd server/application-server && mvn verify -DskipTests=true && node ../../scripts/clean-openapi-specs.js", + "generate:api:application-server-specs": "cd server/application-server && mvn verify -DskipTests=true -Dapp.profiles=specs", "generate:api:application-server-client": "npx openapi-generator-cli generate -i server/application-server/openapi.yaml -g typescript-angular -o webapp/src/app/core/modules/openapi --additional-properties fileNaming=kebab-case,withInterfaces=true --generate-alias-as-model", "generate:api": "npm run generate:api:application-server-specs && npm run generate:api:clean && npm run generate:api:application-server-client" }, diff --git a/scripts/clean-openapi-specs.js b/scripts/clean-openapi-specs.js deleted file mode 100644 index 381e3e80..00000000 --- a/scripts/clean-openapi-specs.js +++ /dev/null @@ -1,25 +0,0 @@ -const fs = require("fs"); -const path = require("path"); - -const filePath = path.join(__dirname, "../server/application-server/openapi.yaml"); - -fs.readFile(filePath, "utf8", (err, data) => { - if (err) { - console.error("Error reading the file:", err); - return; - } - - const pattern = /tags:\n\s*-\s*.*?-controller\b/g; - - // Function to remove '-controller' from the tag - const updatedContent = data.replace(pattern, (match) => { - return match.replace("-controller", ""); - }); - - fs.writeFile(filePath, updatedContent, "utf8", (err) => { - if (err) { - console.error("Error writing the file:", err); - return; - } - }); -}); diff --git a/server/application-server/.vscode/launch.json b/server/application-server/.vscode/launch.json new file mode 100644 index 00000000..634a1211 --- /dev/null +++ b/server/application-server/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "Application", + "request": "launch", + "mainClass": "de.tum.in.www1.hephaestus.Application", + "projectName": "hephaestus", + "env": { + "SPRING_PROFILES_ACTIVE": "local" + }, + } + ] +} \ No newline at end of file diff --git a/server/application-server/compose.yaml b/server/application-server/compose.yaml index fcb704a8..ad5b8703 100644 --- a/server/application-server/compose.yaml +++ b/server/application-server/compose.yaml @@ -11,6 +11,8 @@ services: - '5432:5432' networks: - app-network + volumes: + - ./postgres-data:/var/lib/postgresql/data keycloak: image: quay.io/keycloak/keycloak:26.0.0 diff --git a/server/application-server/openapi.yaml b/server/application-server/openapi.yaml index 146cfc4a..024380c5 100644 --- a/server/application-server/openapi.yaml +++ b/server/application-server/openapi.yaml @@ -13,24 +13,6 @@ servers: - url: / description: Default Server URL paths: - /user/{login}: - get: - tags: - - user - operationId: getUser - parameters: - - name: login - in: path - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: "#/components/schemas/UserDTO" /user/{login}/profile: get: tags: @@ -48,65 +30,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/UserProfileDTO" - /user/{login}/full: - get: - tags: - - user - operationId: getFullUser - parameters: - - name: login - in: path - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: "#/components/schemas/User" - /pullrequest/{id}: - get: - tags: - - pull-request - operationId: getPullRequest - parameters: - - name: id - in: path - required: true - schema: - type: integer - format: int64 - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: "#/components/schemas/PullRequest" - /pullrequest/author/{login}: - get: - tags: - - pull-request - operationId: getPullRequestsByAuthor - parameters: - - name: login - in: path - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequest" + $ref: "#/components/schemas/UserProfile" /meta: get: tags: @@ -118,7 +42,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/MetaDataDTO" + $ref: "#/components/schemas/MetaData" /leaderboard: get: tags: @@ -174,483 +98,269 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/UserInfoDto" + $ref: "#/components/schemas/AuthUserInfo" components: schemas: - IssueCommentDTO: - type: object - properties: - id: - type: integer - format: int64 - body: - type: string - createdAt: - type: string - updatedAt: - type: string - author: - $ref: "#/components/schemas/UserDTO" - pullRequest: - $ref: "#/components/schemas/PullRequestDTO" - PullRequestDTO: + PullRequestInfo: required: - additions - - createdAt + - commentsCount - deletions + - htmlUrl - id + - isDraft + - isMerged - number - state - title - - updatedAt - - url type: object properties: id: type: integer format: int64 - title: - type: string number: type: integer format: int32 - url: + title: type: string state: type: string enum: - - CLOSED - OPEN + - CLOSED + isDraft: + type: boolean + isMerged: + type: boolean + commentsCount: + type: integer + format: int32 + author: + $ref: "#/components/schemas/UserInfo" + labels: + type: array + items: + $ref: "#/components/schemas/LabelInfo" + assignees: + type: array + items: + $ref: "#/components/schemas/UserInfo" + repository: + $ref: "#/components/schemas/RepositoryInfo" additions: type: integer format: int32 deletions: type: integer format: int32 - createdAt: + mergedAt: type: string format: date-time - updatedAt: + closedAt: type: string format: date-time - mergedAt: + htmlUrl: type: string - format: date-time - author: - $ref: "#/components/schemas/UserDTO" - comments: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/IssueCommentDTO" - labels: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequestLabel" - repository: - $ref: "#/components/schemas/RepositoryDTO" - PullRequestLabel: - type: object - properties: - name: + createdAt: type: string - color: + format: date-time + updatedAt: type: string - RepositoryDTO: + format: date-time + LabelInfo: required: + - color + - id - name - - nameWithOwner - - url - type: object - properties: - name: - type: string - nameWithOwner: - type: string - description: - type: string - url: - type: string - UserDTO: type: object properties: id: type: integer format: int64 - login: - type: string - email: - type: string name: type: string - url: + color: + type: string + UserProfile: + required: + - contributedRepositories + - firstContribution + - userInfo + type: object + properties: + userInfo: + $ref: "#/components/schemas/UserInfo" + firstContribution: type: string - pullRequests: - uniqueItems: true + format: date-time + contributedRepositories: type: array items: - $ref: "#/components/schemas/PullRequestDTO" - comments: - uniqueItems: true + $ref: "#/components/schemas/RepositoryInfo" + reviewActivity: type: array items: - $ref: "#/components/schemas/IssueCommentDTO" - PullRequestReviewDTO: + $ref: "#/components/schemas/PullRequestReviewInfo" + openPullRequests: + type: array + items: + $ref: "#/components/schemas/PullRequestInfo" + PullRequestReviewInfo: required: - - createdAt + - codeComments + - htmlUrl - id + - isDismissed - state - - submittedAt - - updatedAt type: object properties: id: type: integer format: int64 - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - submittedAt: - type: string - format: date-time + isDismissed: + type: boolean state: type: string enum: - COMMENTED - APPROVED - CHANGES_REQUESTED - - DISMISSED - url: - type: string + - UNKNOWN + codeComments: + type: integer + format: int32 + author: + $ref: "#/components/schemas/UserInfo" pullRequest: - $ref: "#/components/schemas/PullRequestDTO" - UserProfileDTO: + $ref: "#/components/schemas/PullRequestBaseInfo" + htmlUrl: + type: string + submittedAt: + type: string + format: date-time + RepositoryInfo: required: - - avatarUrl - - firstContribution + - htmlUrl - id - - login - - repositories + - name + - nameWithOwner type: object properties: id: type: integer format: int64 - login: + name: type: string - avatarUrl: + nameWithOwner: type: string - firstContribution: + description: type: string - format: date-time - repositories: - uniqueItems: true + htmlUrl: + type: string + MetaData: + type: object + properties: + repositoriesToMonitor: type: array items: type: string - activity: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequestReviewDTO" - pullRequests: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequestDTO" - IssueComment: - type: object - properties: - id: - type: integer - format: int64 - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - body: - type: string - author: - $ref: "#/components/schemas/User" - pullRequest: - $ref: "#/components/schemas/PullRequest" - PullRequest: + PullRequestBaseInfo: required: + - htmlUrl + - id + - isDraft + - isMerged + - number - state - title - - url type: object properties: id: type: integer format: int64 - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time number: type: integer format: int32 title: type: string - url: - type: string state: type: string - description: |- - State of the PullRequest. - Does not include the state of the merge. enum: - - CLOSED - OPEN - additions: - type: integer - format: int32 - deletions: - type: integer - format: int32 - commits: - type: integer - format: int32 - changedFiles: - type: integer - format: int32 - mergedAt: - type: string - format: date-time - author: - $ref: "#/components/schemas/User" - comments: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/IssueComment" - reviews: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequestReview" + - CLOSED + isDraft: + type: boolean + isMerged: + type: boolean repository: - $ref: "#/components/schemas/Repository" - pullRequestLabels: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequestLabel" - PullRequestReview: - required: - - state - type: object - properties: - id: - type: integer - format: int64 - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - author: - $ref: "#/components/schemas/User" - state: - type: string - enum: - - COMMENTED - - APPROVED - - CHANGES_REQUESTED - - DISMISSED - submittedAt: - type: string - format: date-time - url: + $ref: "#/components/schemas/RepositoryInfo" + htmlUrl: type: string - comments: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequestReviewComment" - pullRequest: - $ref: "#/components/schemas/PullRequest" - PullRequestReviewComment: - required: - - commit - type: object - properties: - id: - type: integer - format: int64 - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - body: - type: string - author: - $ref: "#/components/schemas/User" - review: - $ref: "#/components/schemas/PullRequestReview" - commit: - type: string - Repository: - required: - - defaultBranch - - name - - nameWithOwner - - url - - visibility + AuthUserInfo: type: object properties: - id: - type: integer - format: int64 - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time name: type: string - nameWithOwner: - type: string - description: - type: string - defaultBranch: - type: string - visibility: - type: string - enum: - - PUBLIC - - PRIVATE - url: - type: string - homepage: - type: string - pullRequests: - uniqueItems: true + roles: type: array items: - $ref: "#/components/schemas/PullRequest" - User: + type: string + UserInfo: required: + - avatarUrl + - htmlUrl + - id - login - - type - - url + - name type: object properties: id: type: integer format: int64 - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time login: type: string - description: Unique login identifier for a user. - email: + avatarUrl: type: string name: type: string - description: Display name of the user. - url: - type: string - description: |- - Unique URL to the user's profile. - Not the website a user can set in their profile. - avatarUrl: - type: string - description: |- - URL to the user's avatar. - If unavailable, a fallback can be generated from the login, e.g. on Github: - https://github.com/{login}.png - type: + htmlUrl: type: string - description: Type of the user. Used to distinguish between users and bots. - enum: - - USER - - BOT - pullRequests: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequest" - issueComments: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/IssueComment" - reviewComments: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequestReviewComment" - reviews: - uniqueItems: true - type: array - items: - $ref: "#/components/schemas/PullRequestReview" - MetaDataDTO: - type: object - properties: - repositoriesToMonitor: - type: array - items: - type: string LeaderboardEntry: + required: + - numberOfApprovals + - numberOfChangeRequests + - numberOfCodeComments + - numberOfComments + - numberOfReviewedPRs + - numberOfUnknowns + - rank + - score + - user type: object properties: - githubName: - type: string - avatarUrl: - type: string - name: - type: string - type: - type: string - enum: - - USER - - BOT - score: + rank: type: integer format: int32 - rank: + score: type: integer format: int32 + user: + $ref: "#/components/schemas/UserInfo" numberOfReviewedPRs: type: integer format: int32 - changesRequested: - type: array - items: - $ref: "#/components/schemas/PullRequestReviewDTO" - approvals: - type: array - items: - $ref: "#/components/schemas/PullRequestReviewDTO" - comments: - type: array - items: - $ref: "#/components/schemas/PullRequestReviewDTO" - UserInfoDto: - type: object - properties: - name: - type: string - roles: - type: array - items: - type: string + numberOfApprovals: + type: integer + format: int32 + numberOfChangeRequests: + type: integer + format: int32 + numberOfComments: + type: integer + format: int32 + numberOfUnknowns: + type: integer + format: int32 + numberOfCodeComments: + type: integer + format: int32 diff --git a/server/application-server/pom.xml b/server/application-server/pom.xml index 13d54409..0aaedd6b 100644 --- a/server/application-server/pom.xml +++ b/server/application-server/pom.xml @@ -1,6 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot @@ -31,6 +32,12 @@ 21 local,dev + + + jitpack.io + https://jitpack.io + + org.springframework.boot @@ -109,38 +116,22 @@ 0.15.0 - org.springframework.modulith - spring-modulith-starter-core - - - org.springframework.modulith - spring-modulith-starter-test - test - - - org.kohsuke + com.github.FelixTJDietrich github-api - 1.324 + 2.0.5 io.hypersistence hypersistence-utils-hibernate-63 3.8.2 + + io.nats + jnats + 2.20.2 + - - - - org.springframework.modulith - spring-modulith-bom - 1.2.2 - import - pom - - - - diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/Application.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/Application.java index b56dce19..ccf2da38 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/Application.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/Application.java @@ -2,10 +2,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.modulith.Modulithic; @SpringBootApplication -@Modulithic(systemName = "Hephaestus") public class Application { public static void main(String[] args) { diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/ClassImportIntegratorIntegratorProvider.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/ClassImportIntegratorIntegratorProvider.java index ec7ca8bb..d3180a5d 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/ClassImportIntegratorIntegratorProvider.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/ClassImportIntegratorIntegratorProvider.java @@ -6,10 +6,14 @@ import org.hibernate.integrator.spi.Integrator; import org.hibernate.jpa.boot.spi.IntegratorProvider; -import de.tum.in.www1.hephaestus.codereview.comment.IssueCommentDTO; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequestDTO; -import de.tum.in.www1.hephaestus.codereview.repository.RepositoryDTO; -import de.tum.in.www1.hephaestus.codereview.user.UserDTO; +import de.tum.in.www1.hephaestus.gitprovider.issue.IssueInfoDTO; +import de.tum.in.www1.hephaestus.gitprovider.issuecomment.IssueCommentInfoDTO; +import de.tum.in.www1.hephaestus.gitprovider.label.LabelInfoDTO; +import de.tum.in.www1.hephaestus.gitprovider.milestone.MilestoneInfoDTO; +import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestInfoDTO; +import de.tum.in.www1.hephaestus.gitprovider.pullrequestreview.PullRequestReviewInfoDTO; +import de.tum.in.www1.hephaestus.gitprovider.repository.RepositoryInfoDTO; +import de.tum.in.www1.hephaestus.gitprovider.user.UserInfoDTO; import io.hypersistence.utils.hibernate.type.util.ClassImportIntegrator; public class ClassImportIntegratorIntegratorProvider implements IntegratorProvider { @@ -19,10 +23,14 @@ public List getIntegrators() { // Accessible DTOs @SuppressWarnings("rawtypes") List classes = new ArrayList<>(); - classes.add(UserDTO.class); - classes.add(PullRequestDTO.class); - classes.add(IssueCommentDTO.class); - classes.add(RepositoryDTO.class); + classes.add(UserInfoDTO.class); + classes.add(IssueInfoDTO.class); + classes.add(LabelInfoDTO.class); + classes.add(MilestoneInfoDTO.class); + classes.add(PullRequestInfoDTO.class); + classes.add(IssueCommentInfoDTO.class); + classes.add(PullRequestReviewInfoDTO.class); + classes.add(RepositoryInfoDTO.class); return List.of(new ClassImportIntegrator(classes)); } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/OpenAPIConfiguration.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/OpenAPIConfiguration.java index 3d77032a..a225cbe5 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/OpenAPIConfiguration.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/OpenAPIConfiguration.java @@ -1,5 +1,12 @@ package de.tum.in.www1.hephaestus; +import java.util.Map; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import io.swagger.v3.oas.annotations.OpenAPIDefinition; @@ -7,25 +14,93 @@ import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.info.License; import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.models.media.Schema; @Configuration @OpenAPIDefinition( - info = @Info( - title = "Hephaestus API", - description = "API documentation for the Hephaestus application server.", - version = "0.0.1", - contact = @Contact( - name = "Felix T.J. Dietrich", - email = "felixtj.dietrich@tum.de" + info = @Info( + title = "Hephaestus API", + description = "API documentation for the Hephaestus application server.", + version = "0.0.1", + contact = @Contact( + name = "Felix T.J. Dietrich", + email = "felixtj.dietrich@tum.de" + ), + license = @License( + name = "MIT License", + url = "https://github.com/ls1intum/Hephaestus/blob/develop/LICENSE" + ) ), - license = @License( - name = "MIT License", - url = "https://github.com/ls1intum/Hephaestus/blob/develop/LICENSE" - ) - ), - servers = { - @Server(url = "/", description = "Default Server URL"), - } + servers = { + @Server(url = "/", description = "Default Server URL"), + } ) public class OpenAPIConfiguration { + + Logger logger = LoggerFactory.getLogger(OpenAPIConfiguration.class); + + @Bean + public OpenApiCustomizer schemaCustomizer() { + return openApi -> { + var components = openApi.getComponents(); + + // Only include schemas with DTO suffix and remove the suffix + var schemas = components + .getSchemas() + .entrySet() + .stream() + .filter(entry -> entry.getKey().endsWith("DTO")) + .collect(Collectors.toMap(entry -> entry.getKey().substring(0, entry.getKey().length() - 3), + entry -> { + var schema = entry.getValue(); + schema.setName(entry.getKey().substring(0, entry.getKey().length() - 3)); + return schema; + })); + + // Remove DTO suffix from attribute names + schemas.forEach((key, value) -> { + Map> properties = value.getProperties(); + if (properties != null) { + properties.forEach((propertyKey, propertyValue) -> { + removeDTOSuffixesFromSchemaRecursively(propertyValue); + }); + } + }); + + components.setSchemas(schemas); + + var paths = openApi.getPaths(); + paths.forEach((path, pathItem) -> { + logger.info(path); + pathItem.readOperations().forEach(operation -> { + // Remove DTO suffix from reponse schemas + var responses = operation.getResponses(); + responses.forEach((responseCode, response) -> { + var content = response.getContent(); + content.forEach((contentType, mediaType) -> { + removeDTOSuffixesFromSchemaRecursively(mediaType.getSchema()); + + }); + }); + + // Remove -controller suffix from tags + operation.setTags(operation.getTags() + .stream() + .map(tag -> tag.substring(0, tag.length() - 11)).toList()); + }); + }); + + + }; + } + + private void removeDTOSuffixesFromSchemaRecursively(Schema schema) { + if (schema.get$ref() != null && schema.get$ref().endsWith("DTO")) { + schema.set$ref(schema.get$ref().substring(0, schema.get$ref().length() - 3)); + } + + if (schema.getItems() != null) { + removeDTOSuffixesFromSchemaRecursively(schema.getItems()); + } + } } \ No newline at end of file diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/WebConfig.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/WebConfig.java deleted file mode 100644 index 6afaac48..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/WebConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.tum.in.www1.hephaestus; - -import org.springframework.context.annotation.Configuration; -import org.springframework.format.FormatterRegistry; -import org.springframework.lang.NonNull; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import de.tum.in.www1.hephaestus.codereview.comment.IssueCommentConverter; -import de.tum.in.www1.hephaestus.codereview.comment.review.PullRequestReviewCommentConverter; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequestConverter; -import de.tum.in.www1.hephaestus.codereview.pullrequest.review.PullRequestReviewConverter; -import de.tum.in.www1.hephaestus.codereview.repository.RepositoryConverter; -import de.tum.in.www1.hephaestus.codereview.user.UserConverter; - -@Configuration -public class WebConfig implements WebMvcConfigurer { - - @Override - public void addFormatters(@NonNull FormatterRegistry registry) { - registry.addConverter(new UserConverter()); - registry.addConverter(new RepositoryConverter()); - registry.addConverter(new PullRequestConverter()); - registry.addConverter(new PullRequestReviewConverter()); - registry.addConverter(new IssueCommentConverter()); - registry.addConverter(new PullRequestReviewCommentConverter()); - } -} \ No newline at end of file diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/admin/AdminController.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/admin/AdminController.java index 71acbe47..9c6b1c82 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/admin/AdminController.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/admin/AdminController.java @@ -19,12 +19,12 @@ public String admin() { } @GetMapping("/me") - public UserInfoDto getGretting(JwtAuthenticationToken auth) { - return new UserInfoDto( + public AuthUserInfoDTO getGretting(JwtAuthenticationToken auth) { + return new AuthUserInfoDTO( auth.getToken().getClaimAsString(StandardClaimNames.PREFERRED_USERNAME), auth.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList()); } - public static record UserInfoDto(String name, List roles) { + public static record AuthUserInfoDTO(String name, List roles) { } } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/Comment.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/Comment.java deleted file mode 100644 index abeae890..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/Comment.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.base; - -import de.tum.in.www1.hephaestus.codereview.user.User; -import jakarta.persistence.Column; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.MappedSuperclass; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@MappedSuperclass -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@ToString(callSuper = true) -public abstract class Comment extends BaseGitServiceEntity { - @Column(columnDefinition = "TEXT") - @ToString.Exclude - protected String body; - - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "author_id") - @ToString.Exclude - protected User author; -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueComment.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueComment.java deleted file mode 100644 index 103f68c7..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueComment.java +++ /dev/null @@ -1,25 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.comment; - -import jakarta.persistence.Table; -import de.tum.in.www1.hephaestus.codereview.base.Comment; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequest; -import jakarta.persistence.Entity; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Entity -@Table(name = "issue_comment") -@Getter -@Setter -@NoArgsConstructor -@ToString(callSuper = true) -public class IssueComment extends Comment { - @ManyToOne - @JoinColumn(name = "pullrequest_id", referencedColumnName = "id") - @ToString.Exclude - private PullRequest pullRequest; -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentConverter.java deleted file mode 100644 index 502eb33a..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentConverter.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.comment; - -import org.kohsuke.github.GHIssueComment; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntityConverter; - -@Component -public class IssueCommentConverter extends BaseGitServiceEntityConverter { - - protected static final Logger logger = LoggerFactory.getLogger(IssueCommentConverter.class); - - @Override - public IssueComment convert(@NonNull GHIssueComment source) { - return update(source, new IssueComment()); - } - - @Override - public IssueComment update(@NonNull GHIssueComment source, @NonNull IssueComment comment) { - convertBaseFields(source, comment); - comment.setBody(source.getBody()); - return comment; - } - -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentDTO.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentDTO.java deleted file mode 100644 index 6e2b537f..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentDTO.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.comment; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequestDTO; -import de.tum.in.www1.hephaestus.codereview.user.UserDTO; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record IssueCommentDTO(Long id, String body, String createdAt, String updatedAt, UserDTO author, - PullRequestDTO pullRequest) { - public IssueCommentDTO(Long id, String body, String createdAt, String updatedAt) { - this(id, body, createdAt, updatedAt, null, null); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentRepository.java deleted file mode 100644 index adacc0d7..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/IssueCommentRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.comment; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface IssueCommentRepository extends JpaRepository { - -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/review/PullRequestReviewComment.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/review/PullRequestReviewComment.java deleted file mode 100644 index bdf18610..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/review/PullRequestReviewComment.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.comment.review; - -import org.springframework.lang.NonNull; - -import de.tum.in.www1.hephaestus.codereview.base.Comment; -import de.tum.in.www1.hephaestus.codereview.pullrequest.review.PullRequestReview; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Entity -@Table(name = "pull_request_review_comment") -@Getter -@Setter -@NoArgsConstructor -@ToString(callSuper = true) -public class PullRequestReviewComment extends Comment { - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "review_id", referencedColumnName = "id") - @ToString.Exclude - private PullRequestReview review; - - @NonNull - private String commit; -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/review/PullRequestReviewCommentConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/review/PullRequestReviewCommentConverter.java deleted file mode 100644 index 03982fae..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/comment/review/PullRequestReviewCommentConverter.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.comment.review; - -import org.kohsuke.github.GHPullRequestReviewComment; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntityConverter; - -@Component -public class PullRequestReviewCommentConverter - extends BaseGitServiceEntityConverter { - - protected static final Logger logger = LoggerFactory.getLogger(PullRequestReviewCommentConverter.class); - - @Override - public PullRequestReviewComment convert(@NonNull GHPullRequestReviewComment source) { - return update(source, new PullRequestReviewComment()); - } - - @Override - public PullRequestReviewComment update(@NonNull GHPullRequestReviewComment source, - @NonNull PullRequestReviewComment comment) { - convertBaseFields(source, comment); - comment.setBody(source.getBody()); - comment.setCommit(source.getCommitId()); - return comment; - } - -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/IssueState.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/IssueState.java deleted file mode 100644 index 07ae6da8..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/IssueState.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest; - -public enum IssueState { - CLOSED, OPEN -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequest.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequest.java deleted file mode 100644 index 376f8ce3..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequest.java +++ /dev/null @@ -1,89 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest; - -import java.time.OffsetDateTime; -import java.util.HashSet; -import java.util.Set; - -import jakarta.persistence.*; -import org.springframework.lang.NonNull; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntity; -import de.tum.in.www1.hephaestus.codereview.comment.IssueComment; -import de.tum.in.www1.hephaestus.codereview.pullrequest.review.PullRequestReview; -import de.tum.in.www1.hephaestus.codereview.repository.Repository; -import de.tum.in.www1.hephaestus.codereview.user.User; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Entity -@Table(name = "pull_request") -@Getter -@Setter -@NoArgsConstructor -@ToString(callSuper = true) -public class PullRequest extends BaseGitServiceEntity { - - private int number; - - @NonNull - private String title; - - @NonNull - private String url; - - /** - * State of the PullRequest. - * Does not include the state of the merge. - */ - @NonNull - private IssueState state; - - private int additions; - - private int deletions; - - private int commits; - - private int changedFiles; - - private OffsetDateTime mergedAt; - - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "author_id") - @ToString.Exclude - private User author; - - @OneToMany(cascade = CascadeType.ALL, mappedBy = "pullRequest") - @ToString.Exclude - private Set comments = new HashSet<>(); - - @OneToMany(cascade = CascadeType.REFRESH, mappedBy = "pullRequest") - @ToString.Exclude - private Set reviews = new HashSet<>(); - - @ManyToOne - @JoinColumn(name = "repository_id", referencedColumnName = "id") - @ToString.Exclude - private Repository repository; - - @ElementCollection - private Set pullRequestLabels = new HashSet<>(); - - public void addComment(IssueComment comment) { - comments.add(comment); - } - - public void addReview(PullRequestReview review) { - reviews.add(review); - } - - public Integer getAdditions() { - return additions; - } - - public Integer getDeletions() { - return deletions; - } -} \ No newline at end of file diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestController.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestController.java deleted file mode 100644 index 8af92a91..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestController.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest; - -import java.util.Optional; -import java.util.Set; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/pullrequest") -public class PullRequestController { - private final PullRequestService pullRequestService; - - public PullRequestController(PullRequestService pullRequestService) { - this.pullRequestService = pullRequestService; - } - - @GetMapping("/{id}") - public ResponseEntity getPullRequest(@PathVariable Long id) { - Optional pullRequest =pullRequestService.getPullRequestById(id); - return pullRequest.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); - } - - @GetMapping("/author/{login}") - public Set getPullRequestsByAuthor(@PathVariable String login) { - return pullRequestService.getPullRequestsByAuthor(login); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestConverter.java deleted file mode 100644 index 22dbcdcf..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestConverter.java +++ /dev/null @@ -1,86 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest; - -import java.io.IOException; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import org.kohsuke.github.GHLabel; -import org.kohsuke.github.GHPullRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntityConverter; - -@Component -public class PullRequestConverter extends BaseGitServiceEntityConverter { - - protected static final Logger logger = LoggerFactory.getLogger(PullRequestConverter.class); - - @Override - public PullRequest convert(@NonNull GHPullRequest source) { - PullRequest pullRequest = new PullRequest(); - pullRequest.setNumber(source.getNumber()); - pullRequest.setUrl(source.getHtmlUrl().toString()); - return update(source, pullRequest); - } - - @Override - public PullRequest update(@NonNull GHPullRequest source, @NonNull PullRequest pullRequest) { - convertBaseFields(source, pullRequest); - pullRequest.setTitle(source.getTitle()); - pullRequest.setState(convertState(source.getState())); - pullRequest.setPullRequestLabels(convertLabels(source.getLabels())); - - try { - pullRequest.setAdditions(source.getAdditions()); - } catch (IOException e) { - logger.error("Failed to convert additions field for source {}: {}", source.getId(), e.getMessage()); - pullRequest.setAdditions(0); - } - try { - pullRequest.setDeletions(source.getDeletions()); - } catch (IOException e) { - logger.error("Failed to convert deletions field for source {}: {}", source.getId(), e.getMessage()); - pullRequest.setDeletions(0); - } - try { - pullRequest.setCommits(source.getCommits()); - } catch (IOException e) { - logger.error("Failed to convert commits field for source {}: {}", source.getId(), e.getMessage()); - pullRequest.setCommits(0); - } - try { - pullRequest.setChangedFiles(source.getChangedFiles()); - } catch (IOException e) { - logger.error("Failed to convert changedFiles field for source {}: {}", source.getId(), e.getMessage()); - pullRequest.setChangedFiles(0); - } - if (source.getMergedAt() != null) { - pullRequest.setMergedAt(convertToOffsetDateTime(source.getMergedAt())); - } - return pullRequest; - } - - private IssueState convertState(org.kohsuke.github.GHIssueState state) { - switch (state) { - case OPEN: - return IssueState.OPEN; - case CLOSED: - return IssueState.CLOSED; - default: - return IssueState.OPEN; - } - } - - private Set convertLabels(Collection labels) { - Set pullRequestLabels = new HashSet<>(); - for (GHLabel label : labels) { - PullRequestLabel pullRequestLabel = new PullRequestLabel(label.getName(), label.getColor()); - pullRequestLabels.add(pullRequestLabel); - } - return pullRequestLabels; - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestDTO.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestDTO.java deleted file mode 100644 index 81ff3023..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestDTO.java +++ /dev/null @@ -1,36 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest; - -import java.time.OffsetDateTime; -import java.util.Set; - -import org.springframework.lang.NonNull; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.hephaestus.codereview.comment.IssueCommentDTO; -import de.tum.in.www1.hephaestus.codereview.repository.RepositoryDTO; -import de.tum.in.www1.hephaestus.codereview.user.UserDTO; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PullRequestDTO(@NonNull Long id, @NonNull String title, @NonNull Integer number, @NonNull String url, - @NonNull IssueState state, @NonNull Integer additions, @NonNull Integer deletions, - @NonNull OffsetDateTime createdAt, @NonNull OffsetDateTime updatedAt, OffsetDateTime mergedAt, - UserDTO author, Set comments, Set labels, RepositoryDTO repository) { - public PullRequestDTO(@NonNull Long id, @NonNull String title, @NonNull Integer number, @NonNull String url, - @NonNull IssueState state, @NonNull Integer additions, @NonNull Integer deletions, - @NonNull OffsetDateTime createdAt, @NonNull OffsetDateTime updatedAt, OffsetDateTime mergedAt) { - this(id, title, number, url, state, additions, deletions, createdAt, updatedAt, mergedAt, null, null, - null, - null); - } - - public PullRequestDTO(@NonNull Long id, @NonNull String title, @NonNull Integer number, @NonNull String url, - @NonNull IssueState state, @NonNull Integer additions, - @NonNull Integer deletions, @NonNull OffsetDateTime createdAt, - @NonNull OffsetDateTime updatedAt, OffsetDateTime mergedAt, - @NonNull Set labels, - @NonNull RepositoryDTO repository) { - this(id, title, number, url, state, additions, deletions, createdAt, updatedAt, mergedAt, null, null, - labels, repository); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestLabel.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestLabel.java deleted file mode 100644 index 1dd201f8..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestLabel.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest; - -import jakarta.persistence.Embeddable; -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; -import lombok.Getter; -import lombok.NonNull; -import lombok.Setter; - -@Embeddable -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -public class PullRequestLabel { - @NonNull - private String name; - - @NonNull - private String color; -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestRepository.java deleted file mode 100644 index abcab783..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest; - -import java.util.Optional; -import java.util.Set; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; - -@Repository -public interface PullRequestRepository extends JpaRepository { - - Set findByAuthor_Login(String authorLogin); - - @Query(""" - SELECT p - FROM PullRequest p - JOIN FETCH p.comments - JOIN FETCH p.reviews - WHERE p.id = :id - """) - Optional findByIdWithEagerRelations(Long id); -} \ No newline at end of file diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestService.java deleted file mode 100644 index 4c3fdbf3..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/PullRequestService.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest; - -import java.util.Optional; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; - -@Service -public class PullRequestService { - private static final Logger logger = LoggerFactory.getLogger(PullRequestService.class); - - private final PullRequestRepository pullRequestRepository; - - public PullRequestService(PullRequestRepository pullRequestRepository) { - this.pullRequestRepository = pullRequestRepository; - } - - public Optional getPullRequestById(Long id) { - logger.info("Getting pullRequest with id: {}", id); - return pullRequestRepository.findById(id); - } - - public Set getPullRequestsByAuthor(String login) { - logger.info("Getting pullRequest by author: {}", login); - return pullRequestRepository.findByAuthor_Login(login); - } - -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReview.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReview.java deleted file mode 100644 index 8361fff4..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReview.java +++ /dev/null @@ -1,57 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest.review; - -import java.time.OffsetDateTime; -import java.util.HashSet; -import java.util.Set; - -import org.springframework.lang.NonNull; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntity; -import de.tum.in.www1.hephaestus.codereview.comment.review.PullRequestReviewComment; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequest; -import de.tum.in.www1.hephaestus.codereview.user.User; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Entity -@Table(name = "pull_request_review") -@Getter -@Setter -@NoArgsConstructor -@ToString(callSuper = true) -public class PullRequestReview extends BaseGitServiceEntity { - - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "author_id") - @ToString.Exclude - private User author; - - @NonNull - private PullRequestReviewState state; - - private OffsetDateTime submittedAt; - - private String url; - - @OneToMany(cascade = CascadeType.REFRESH, mappedBy = "review") - @ToString.Exclude - private Set comments = new HashSet<>(); - - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "pullrequest_id", referencedColumnName = "id") - @ToString.Exclude - private PullRequest pullRequest; - - public void addComment(PullRequestReviewComment comment) { - comments.add(comment); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewConverter.java deleted file mode 100644 index 36a4d483..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewConverter.java +++ /dev/null @@ -1,50 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest.review; - -import java.io.IOException; - -import org.kohsuke.github.GHPullRequestReview; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntityConverter; - -@Component -public class PullRequestReviewConverter extends BaseGitServiceEntityConverter { - - protected static final Logger logger = LoggerFactory.getLogger(PullRequestReviewConverter.class); - - @Override - public PullRequestReview convert(@NonNull GHPullRequestReview source) { - return update(source, new PullRequestReview()); - } - - @Override - public PullRequestReview update(@NonNull GHPullRequestReview source, @NonNull PullRequestReview review) { - convertBaseFields(source, review); - review.setState(convertState(source.getState())); - review.setUrl(source.getHtmlUrl().toString()); - try { - review.setSubmittedAt(convertToOffsetDateTime(source.getSubmittedAt())); - } catch (IOException e) { - logger.error("Failed to convert submittedAt field for source {}: {}", source.getId(), e.getMessage()); - } - return review; - } - - private PullRequestReviewState convertState(org.kohsuke.github.GHPullRequestReviewState state) { - switch (state) { - case COMMENTED: - return PullRequestReviewState.COMMENTED; - case APPROVED: - return PullRequestReviewState.APPROVED; - case CHANGES_REQUESTED: - return PullRequestReviewState.CHANGES_REQUESTED; - case DISMISSED: - return PullRequestReviewState.DISMISSED; - default: - return PullRequestReviewState.COMMENTED; - } - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewDTO.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewDTO.java deleted file mode 100644 index b7dc0451..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewDTO.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest.review; - -import java.time.OffsetDateTime; - -import org.springframework.lang.NonNull; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequestDTO; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PullRequestReviewDTO(@NonNull Long id, @NonNull OffsetDateTime createdAt, - @NonNull OffsetDateTime updatedAt, @NonNull OffsetDateTime submittedAt, - @NonNull PullRequestReviewState state, String url, PullRequestDTO pullRequest) { - public PullRequestReviewDTO(@NonNull Long id, @NonNull OffsetDateTime createdAt, - @NonNull OffsetDateTime updatedAt, @NonNull OffsetDateTime submittedAt, - @NonNull PullRequestReviewState state) { - this(id, createdAt, updatedAt, submittedAt, state, null, null); - } - - public PullRequestReviewDTO(@NonNull Long id, @NonNull OffsetDateTime createdAt, - @NonNull OffsetDateTime updatedAt, @NonNull OffsetDateTime submittedAt, - @NonNull PullRequestReviewState state, String url) { - this(id, createdAt, updatedAt, submittedAt, state, url, null); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewRepository.java deleted file mode 100644 index ad2d2ccd..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest.review; - -import java.util.Optional; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; - -@Repository -public interface PullRequestReviewRepository extends JpaRepository { - - Optional findByAuthor_Login(String authorLogin); - - Optional findByPullRequest(PullRequestReview pullRequest); - - @Query(""" - SELECT pr - FROM PullRequestReview pr - JOIN FETCH pr.comments - WHERE pr.id = :reviewId - """) - Optional findByIdWithEagerComments(Long reviewId); -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewState.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewState.java deleted file mode 100644 index 5d05cb81..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/pullrequest/review/PullRequestReviewState.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.pullrequest.review; - -public enum PullRequestReviewState { - COMMENTED, APPROVED, CHANGES_REQUESTED, DISMISSED -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/Repository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/Repository.java deleted file mode 100644 index f5137cfc..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/Repository.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.repository; - -import java.util.HashSet; -import java.util.Set; - -import org.springframework.lang.NonNull; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntity; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequest; -import io.micrometer.common.lang.Nullable; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Entity; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Entity -@Table(name = "repository") -@Getter -@Setter -@NoArgsConstructor -@ToString(callSuper = true) -public class Repository extends BaseGitServiceEntity { - @NonNull - private String name; - - @NonNull - private String nameWithOwner; - - @Nullable - private String description; - - @NonNull - String defaultBranch; - - @NonNull - private RepositoryVisibility visibility; - - @NonNull - private String url; - - String homepage; - - @OneToMany(cascade = CascadeType.REFRESH, mappedBy = "repository") - @ToString.Exclude - private Set pullRequests = new HashSet<>(); - - public void addPullRequest(PullRequest pullRequest) { - pullRequests.add(pullRequest); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryConverter.java deleted file mode 100644 index 7cb69e9d..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryConverter.java +++ /dev/null @@ -1,45 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.repository; - -import org.kohsuke.github.GHRepository; -import org.kohsuke.github.GHRepository.Visibility; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntityConverter; - -@Component -public class RepositoryConverter extends BaseGitServiceEntityConverter { - - protected static final Logger logger = LoggerFactory.getLogger(RepositoryConverter.class); - - @Override - public Repository convert(@NonNull GHRepository source) { - return update(source, new Repository()); - } - - @Override - public Repository update(@NonNull GHRepository source, @NonNull Repository repository) { - convertBaseFields(source, repository); - repository.setName(source.getName()); - repository.setNameWithOwner(source.getFullName()); - repository.setUrl(source.getHtmlUrl().toString()); - repository.setDescription(source.getDescription()); - repository.setDefaultBranch(source.getDefaultBranch()); - repository.setVisibility(convertVisibility(source.getVisibility())); - repository.setHomepage(source.getHomepage()); - return repository; - } - - private RepositoryVisibility convertVisibility(Visibility visibility) { - switch (visibility) { - case PRIVATE: - return RepositoryVisibility.PRIVATE; - case PUBLIC: - return RepositoryVisibility.PUBLIC; - default: - return RepositoryVisibility.PRIVATE; - } - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryDTO.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryDTO.java deleted file mode 100644 index 712eca0a..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryDTO.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.repository; - -import java.util.Set; - -import org.springframework.lang.NonNull; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequestDTO; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record RepositoryDTO(@NonNull String name, @NonNull String nameWithOwner, String description, - @NonNull String url, - Set pullRequests) { - public RepositoryDTO(@NonNull String name, @NonNull String nameWithOwner, String description, @NonNull String url) { - this(name, nameWithOwner, description, url, null); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryRepository.java deleted file mode 100644 index dc25104e..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryRepository.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; - -@org.springframework.stereotype.Repository -public interface RepositoryRepository - extends JpaRepository { - - Repository findByNameWithOwner(String nameWithOwner); - - @Query(""" - SELECT r - FROM Repository r - JOIN FETCH r.pullRequests - WHERE r.nameWithOwner = :nameWithOwner - """) - Repository findByNameWithOwnerWithEagerPullRequests(String nameWithOwner); -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryVisibility.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryVisibility.java deleted file mode 100644 index 83143a1b..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/repository/RepositoryVisibility.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.repository; - -public enum RepositoryVisibility { - PUBLIC, PRIVATE -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/User.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/User.java deleted file mode 100644 index 8c84ec51..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/User.java +++ /dev/null @@ -1,93 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.user; - -import jakarta.persistence.Table; - -import java.util.HashSet; -import java.util.Set; - -import org.springframework.lang.NonNull; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntity; -import de.tum.in.www1.hephaestus.codereview.comment.IssueComment; -import de.tum.in.www1.hephaestus.codereview.comment.review.PullRequestReviewComment; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequest; -import de.tum.in.www1.hephaestus.codereview.pullrequest.review.PullRequestReview; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.OneToMany; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Entity -@Table(name = "user", schema = "public") -@Getter -@Setter -@NoArgsConstructor -@ToString(callSuper = true) -public class User extends BaseGitServiceEntity { - /** - * Unique login identifier for a user. - */ - @NonNull - private String login; - - @Column - private String email; - - /** - * Display name of the user. - */ - @Column - private String name; - - /** - * Unique URL to the user's profile. - * Not the website a user can set in their profile. - */ - @NonNull - private String url; - - /** - * URL to the user's avatar. - * If unavailable, a fallback can be generated from the login, e.g. on Github: - * https://github.com/{login}.png - */ - private String avatarUrl; - - /** - * Type of the user. Used to distinguish between users and bots. - */ - @NonNull - private UserType type; - - @OneToMany(cascade = CascadeType.ALL, mappedBy = "author") - private Set pullRequests = new HashSet<>(); - - @OneToMany(cascade = CascadeType.ALL, mappedBy = "author") - private Set issueComments = new HashSet<>(); - - @OneToMany(cascade = CascadeType.ALL, mappedBy = "author") - private Set reviewComments = new HashSet<>(); - - @OneToMany(cascade = CascadeType.ALL, mappedBy = "author") - private Set reviews = new HashSet<>(); - - public void addIssueComment(IssueComment comment) { - issueComments.add(comment); - } - - public void addReviewComment(PullRequestReviewComment comment) { - reviewComments.add(comment); - } - - public void addPullRequest(PullRequest pullRequest) { - pullRequests.add(pullRequest); - } - - public void addReview(PullRequestReview review) { - reviews.add(review); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserConverter.java deleted file mode 100644 index d618ab88..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserConverter.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.user; - -import java.io.IOException; - -import org.kohsuke.github.GHUser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntityConverter; - -@Component -public class UserConverter extends BaseGitServiceEntityConverter { - - protected static final Logger logger = LoggerFactory.getLogger(UserConverter.class); - - @Override - public User convert(@NonNull GHUser source) { - return update(source, new User()); - } - - @Override - public User update(@NonNull GHUser source, @NonNull User user) { - convertBaseFields(source, user); - user.setLogin(source.getLogin()); - user.setUrl(source.getHtmlUrl().toString()); - user.setAvatarUrl(source.getAvatarUrl()); - try { - user.setEmail(source.getEmail()); - } catch (IOException e) { - logger.error("Failed to convert user email field for source {}: {}", source.getId(), e.getMessage()); - } - try { - user.setName(source.getName() != null ? source.getName() : source.getLogin()); - } catch (IOException e) { - logger.error("Failed to convert user name field for source {}: {}", source.getId(), e.getMessage()); - } - try { - user.setType(convertUserType(source.getType())); - } catch (IOException e) { - logger.error("Failed to convert user type field for source {}: {}", source.getId(), e.getMessage()); - } - return user; - } - - private UserType convertUserType(String type) { - switch (type) { - case "User": - return UserType.USER; - case "Bot": - return UserType.BOT; - default: - return UserType.USER; - } - } - -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserDTO.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserDTO.java deleted file mode 100644 index 64d8051b..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.user; - -import java.util.Set; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.hephaestus.codereview.comment.IssueCommentDTO; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequestDTO; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record UserDTO(Long id, String login, String email, String name, String url, Set pullRequests, - Set comments) { - public UserDTO(Long id, String login, String email, String name, String url) { - this(id, login, email, name, url, null, null); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserProfileDTO.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserProfileDTO.java deleted file mode 100644 index 07f92fd3..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserProfileDTO.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.user; - -import java.time.OffsetDateTime; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import org.springframework.lang.NonNull; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequestDTO; -import de.tum.in.www1.hephaestus.codereview.pullrequest.review.PullRequestReviewDTO; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record UserProfileDTO(@NonNull Long id, @NonNull String login, @NonNull String avatarUrl, - @NonNull OffsetDateTime firstContribution, @NonNull Set repositories, - Set activity, Set pullRequests) { - public UserProfileDTO(@NonNull Long id, @NonNull String login, @NonNull String avatarUrl, - @NonNull OffsetDateTime firstContribution, @NonNull List repositories) { - this(id, login, avatarUrl, firstContribution, repositories.stream().collect(Collectors.toSet()), null, null); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserRepository.java deleted file mode 100644 index d671a475..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserRepository.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.user; - -import java.time.OffsetDateTime; -import java.util.List; -import java.util.Optional; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -public interface UserRepository extends JpaRepository { - - @Query("SELECT u FROM User u WHERE u.login = :login") - Optional findUser(@Param("login") String login); - - @Query(""" - SELECT u - FROM User u - JOIN FETCH u.pullRequests - JOIN FETCH u.issueComments - JOIN FETCH u.reviewComments - JOIN FETCH u.reviews - WHERE u.login = :login - """) - Optional findUserEagerly(@Param("login") String login); - - @Query(""" - SELECT new UserDTO(u.id, u.login, u.email, u.name, u.url) - FROM User u - WHERE u.login = :login - """) - Optional findByLogin(@Param("login") String login); - - @Query(""" - SELECT u - FROM User u - JOIN FETCH u.pullRequests - JOIN FETCH u.issueComments - JOIN FETCH u.reviewComments - JOIN FETCH u.reviews - """) - List findAllWithRelations(); - - @Query(""" - SELECT u - FROM User u - JOIN FETCH u.reviews re - WHERE re.createdAt BETWEEN :after AND :before - AND (:repository IS NULL OR re.pullRequest.repository.nameWithOwner = :repository) - """) - List findAllInTimeframe(@Param("after") OffsetDateTime after, @Param("before") OffsetDateTime before, - @Param("repository") Optional repository); -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserService.java deleted file mode 100644 index 6d6acd17..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserService.java +++ /dev/null @@ -1,116 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.user; - -import java.time.OffsetDateTime; -import java.util.Comparator; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; - -import de.tum.in.www1.hephaestus.codereview.base.BaseGitServiceEntity; -import de.tum.in.www1.hephaestus.codereview.pullrequest.IssueState; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequest; -import de.tum.in.www1.hephaestus.codereview.pullrequest.PullRequestDTO; -import de.tum.in.www1.hephaestus.codereview.pullrequest.review.PullRequestReview; -import de.tum.in.www1.hephaestus.codereview.pullrequest.review.PullRequestReviewDTO; -import de.tum.in.www1.hephaestus.codereview.repository.RepositoryDTO; - -@Service -public class UserService { - private static final Logger logger = LoggerFactory.getLogger(UserService.class); - - private final UserRepository userRepository; - - public UserService(UserRepository actorRepository) { - this.userRepository = actorRepository; - } - - public Optional getUser(String login) { - logger.info("Getting user with login: " + login); - return userRepository.findUser(login); - } - - public Optional getUserDTO(String login) { - logger.info("Getting userDTO with login: " + login); - return userRepository.findByLogin(login); - } - - public List getAllUsers() { - logger.info("Getting all users"); - return userRepository.findAll().stream().toList(); - } - - public List getAllUsersInTimeframe(OffsetDateTime after, OffsetDateTime before, Optional repository) { - logger.info("Getting all users in timeframe between " + after + " and " + before + " for repository: " - + repository.orElse("all")); - return userRepository.findAllInTimeframe(after, before, repository); - } - - public Optional getUserProfileDTO(String login) { - logger.info("Getting userProfileDTO with login: " + login); - Optional optionalUser = userRepository.findUser(login); - if (optionalUser.isEmpty()) { - return Optional.empty(); - } - User user = optionalUser.get(); - - OffsetDateTime firstContribution = user.getPullRequests().stream().map(pr -> pr.getCreatedAt()) - .min(OffsetDateTime::compareTo).orElse(null); - Set repositories = mapToDTO(user.getPullRequests(), pr -> true, - pr -> pr.getRepository().getNameWithOwner(), - (r1, r2) -> r1.compareTo(r2)); - Set pullRequests = getPullRequestDTOs(user.getPullRequests()); - Set activity = getPullRequestReviewDTOs(user.getReviews()); - - return Optional.of(new UserProfileDTO(user.getId(), user.getLogin(), user.getAvatarUrl(), firstContribution, - repositories, activity, pullRequests)); - } - - private Set getPullRequestDTOs(Set pullRequests) { - return mapToDTO(pullRequests, - isRecentlyPredicate().and(pr -> ((PullRequest) pr).getState().equals(IssueState.OPEN)), - pr -> new PullRequestDTO( - pr.getId(), pr.getTitle(), pr.getNumber(), pr.getUrl(), pr.getState(), pr.getAdditions(), - pr.getDeletions(), - pr.getCreatedAt(), pr.getUpdatedAt(), null, - pr.getPullRequestLabels(), - new RepositoryDTO(pr.getRepository().getName(), - pr.getRepository().getNameWithOwner(), null, - pr.getRepository().getUrl())), - (pr1, pr2) -> pr1.createdAt().compareTo(pr2.createdAt())); - } - - private Set getPullRequestReviewDTOs(Set reviews) { - return mapToDTO(reviews, isRecentlyPredicate(), re -> { - PullRequest pr = re.getPullRequest(); - return new PullRequestReviewDTO(re.getId(), - re.getCreatedAt(), re.getUpdatedAt(), re.getSubmittedAt(), re.getState(), re.getUrl(), - new PullRequestDTO(pr.getId(), pr.getTitle(), pr.getNumber(), pr.getUrl(), pr.getState(), - pr.getAdditions(), pr.getDeletions(), pr.getCreatedAt(), pr.getUpdatedAt(), null, - new HashSet<>(), - new RepositoryDTO(pr.getRepository().getName(), - pr.getRepository().getNameWithOwner(), null, - pr.getRepository().getUrl()))); - }, (prr1, prr2) -> prr2.submittedAt().compareTo(prr1.submittedAt())); - } - - private Set mapToDTO(Set entities, Predicate predicate, - Function mapper, - Comparator comparator) { - return entities.stream().filter(predicate).map(mapper).sorted(comparator) - .collect(Collectors.toCollection(LinkedHashSet::new)); - } - - // Predicate filtering createdAt for within past 7 days - private Predicate isRecentlyPredicate() { - return entity -> entity.getCreatedAt().isAfter(OffsetDateTime.now().minusDays(7)); - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserType.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserType.java deleted file mode 100644 index 26705808..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/user/UserType.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.tum.in.www1.hephaestus.codereview.user; - -public enum UserType { - USER, BOT -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/GitHubConfig.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/GitHubConfig.java new file mode 100644 index 00000000..b08683cf --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/GitHubConfig.java @@ -0,0 +1,49 @@ +package de.tum.in.www1.hephaestus.config; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubBuilder; + +@Configuration +public class GitHubConfig { + + private static final Logger logger = LoggerFactory.getLogger(GitHubConfig.class); + + @Value("${github.authToken}") + private String ghAuthToken; + + @Autowired + private Environment environment; + + @Bean + public GitHub gitHubClient() { + if (environment.matchesProfiles("specs")) { + logger.info("GitHub client is disabled in specs profile"); + return GitHub.offline(); + } + + if (ghAuthToken == null || ghAuthToken.isEmpty()) { + logger.error("GitHub auth token is not provided!"); + throw new IllegalArgumentException("GitHub auth token is required"); + } + try { + GitHub github = new GitHubBuilder().withOAuthToken(ghAuthToken).build(); + if (!github.isCredentialValid()) { + logger.error("Invalid GitHub credentials!"); + throw new IllegalStateException("Invalid GitHub credentials"); + } + return github; + } catch (IOException e) { + logger.error("Failed to initialize GitHub client: {}", e.getMessage()); + throw new RuntimeException("GitHub client initialization failed", e); + } + } +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/NatsConfig.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/NatsConfig.java new file mode 100644 index 00000000..36667e62 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/NatsConfig.java @@ -0,0 +1,31 @@ +package de.tum.in.www1.hephaestus.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.client.Options; + +@Configuration +public class NatsConfig { + + @Value("${nats.server}") + private String natsServer; + + @Autowired + private Environment environment; + + @Bean + public Connection natsConnection() throws Exception { + if (environment.matchesProfiles("specs")) { + return null; + } + + Options options = Options.builder().server(natsServer).build(); + return Nats.connect(options); + } +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/SpringAsyncConfig.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/SpringAsyncConfig.java new file mode 100644 index 00000000..844e4789 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/SpringAsyncConfig.java @@ -0,0 +1,8 @@ +package de.tum.in.www1.hephaestus.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; + +@Configuration +@EnableAsync +public class SpringAsyncConfig { } \ No newline at end of file diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/AuthorAssociation.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/AuthorAssociation.java new file mode 100644 index 00000000..a8f6be60 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/AuthorAssociation.java @@ -0,0 +1,13 @@ +package de.tum.in.www1.hephaestus.gitprovider.common; + +// How the author is associated with the repository. +public enum AuthorAssociation { + COLLABORATOR, + CONTRIBUTOR, + FIRST_TIMER, + FIRST_TIME_CONTRIBUTOR, + MANNEQUIN, + MEMBER, + NONE, + OWNER +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/BaseGitServiceEntity.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java similarity index 90% rename from server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/BaseGitServiceEntity.java rename to server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java index d66dc62f..d739c520 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/BaseGitServiceEntity.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java @@ -1,4 +1,4 @@ -package de.tum.in.www1.hephaestus.codereview.base; +package de.tum.in.www1.hephaestus.gitprovider.common; import java.time.OffsetDateTime; diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/BaseGitServiceEntityConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntityConverter.java similarity index 74% rename from server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/BaseGitServiceEntityConverter.java rename to server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntityConverter.java index 5ba3e8b8..c286f0b3 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/codereview/base/BaseGitServiceEntityConverter.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntityConverter.java @@ -1,4 +1,6 @@ -package de.tum.in.www1.hephaestus.codereview.base; +package de.tum.in.www1.hephaestus.gitprovider.common; + +import java.io.IOException; import org.kohsuke.github.GHObject; import org.springframework.core.convert.converter.Converter; @@ -6,10 +8,6 @@ import org.springframework.lang.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.Date; @ReadingConverter public abstract class BaseGitServiceEntityConverter @@ -28,21 +26,17 @@ protected void convertBaseFields(S source, T target) { target.setId(source.getId()); try { - target.setCreatedAt(convertToOffsetDateTime(source.getCreatedAt())); + target.setCreatedAt(DateUtil.convertToOffsetDateTime(source.getCreatedAt())); } catch (IOException e) { logger.error("Failed to convert createdAt field for source {}: {}", source.getId(), e.getMessage()); target.setCreatedAt(null); } try { - target.setUpdatedAt(convertToOffsetDateTime(source.getUpdatedAt())); + target.setUpdatedAt(DateUtil.convertToOffsetDateTime(source.getUpdatedAt())); } catch (IOException e) { logger.error("Failed to convert updatedAt field for source {}: {}", source.getId(), e.getMessage()); target.setUpdatedAt(null); } } - - protected OffsetDateTime convertToOffsetDateTime(Date date) { - return date != null ? date.toInstant().atOffset(ZoneOffset.UTC) : null; - } } \ No newline at end of file diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/DateUtil.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/DateUtil.java new file mode 100644 index 00000000..41b49feb --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/DateUtil.java @@ -0,0 +1,11 @@ +package de.tum.in.www1.hephaestus.gitprovider.common; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Date; + +public class DateUtil { + public static OffsetDateTime convertToOffsetDateTime(Date date) { + return date != null ? date.toInstant().atOffset(ZoneOffset.UTC) : null; + } +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubAuthorAssociationConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubAuthorAssociationConverter.java new file mode 100644 index 00000000..1b94800a --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubAuthorAssociationConverter.java @@ -0,0 +1,39 @@ +package de.tum.in.www1.hephaestus.gitprovider.common.github; + +import org.kohsuke.github.GHCommentAuthorAssociation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; + +import de.tum.in.www1.hephaestus.gitprovider.common.AuthorAssociation; + +@Component +public class GitHubAuthorAssociationConverter implements Converter { + + private static final Logger logger = LoggerFactory.getLogger(GitHubAuthorAssociationConverter.class); + + @Override + public AuthorAssociation convert(@NonNull GHCommentAuthorAssociation source) { + switch (source) { + case COLLABORATOR: + return AuthorAssociation.COLLABORATOR; + case CONTRIBUTOR: + return AuthorAssociation.CONTRIBUTOR; + case FIRST_TIME_CONTRIBUTOR: + return AuthorAssociation.FIRST_TIME_CONTRIBUTOR; + case FIRST_TIMER: + return AuthorAssociation.FIRST_TIMER; + case MEMBER: + return AuthorAssociation.MEMBER; + case NONE: + return AuthorAssociation.NONE; + case OWNER: + return AuthorAssociation.OWNER; + default: + logger.error("Unknown author association: {}", source); + return AuthorAssociation.NONE; + } + } +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubMessageHandler.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubMessageHandler.java new file mode 100644 index 00000000..8600be8e --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubMessageHandler.java @@ -0,0 +1,62 @@ +package de.tum.in.www1.hephaestus.gitprovider.common.github; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; + +import org.kohsuke.github.GHEvent; +import org.kohsuke.github.GHEventPayload; +import org.kohsuke.github.GitHub; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.nats.client.Message; +import io.nats.client.MessageHandler; +import org.springframework.stereotype.Component; + +@Component +public abstract class GitHubMessageHandler implements MessageHandler { + + private static final Logger logger = LoggerFactory.getLogger(GitHubMessageHandler.class); + + private final Class payloadType; + + protected GitHubMessageHandler(Class payloadType) { + this.payloadType = payloadType; + } + + @Override + public void onMessage(Message msg) { + String eventType = getHandlerEvent().name().toLowerCase(); + String subject = msg.getSubject(); + if (!subject.endsWith(eventType)) { + logger.error("Received message on unexpected subject: {}, expected to end with {}", subject, eventType); + return; + } + + String payload = new String(msg.getData(), StandardCharsets.UTF_8); + + try (StringReader reader = new StringReader(payload)) { + T eventPayload = GitHub.offline().parseEventPayload(reader, payloadType); + handleEvent(eventPayload); + } catch (IOException e) { + logger.error("Failed to parse payload for subject {}: {}", subject, e.getMessage(), e); + } catch (Exception e) { + logger.error("Unexpected error while handling message for subject {}: {}", subject, e.getMessage(), e); + } + } + + /** + * Handles the parsed event payload. + * + * @param eventPayload The parsed GHEventPayload. + */ + protected abstract void handleEvent(T eventPayload); + + /** + * Returns the GHEvent that this handler is responsible for. + * + * @return The GHEvent. + */ + protected abstract GHEvent getHandlerEvent(); +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubMessageHandlerRegistry.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubMessageHandlerRegistry.java new file mode 100644 index 00000000..35126190 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/GitHubMessageHandlerRegistry.java @@ -0,0 +1,29 @@ +package de.tum.in.www1.hephaestus.gitprovider.common.github; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.kohsuke.github.GHEvent; +import org.springframework.stereotype.Component; + +@Component +public class GitHubMessageHandlerRegistry { + + private final Map> handlerMap = new HashMap<>(); + + public GitHubMessageHandlerRegistry(GitHubMessageHandler[] handlers) { + for (GitHubMessageHandler handler : handlers) { + handlerMap.put(handler.getHandlerEvent(), handler); + } + } + + public GitHubMessageHandler getHandler(GHEvent eventType) { + return handlerMap.get(eventType); + } + + public List getSupportedEvents() { + return new ArrayList<>(handlerMap.keySet()); + } +} \ No newline at end of file diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/Issue.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/Issue.java new file mode 100644 index 00000000..493386dd --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/Issue.java @@ -0,0 +1,113 @@ +package de.tum.in.www1.hephaestus.gitprovider.issue; + +import java.util.HashSet; +import java.util.Set; +import java.time.OffsetDateTime; + +import org.springframework.lang.NonNull; + +import jakarta.persistence.Table; +import jakarta.persistence.CascadeType; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.DiscriminatorType; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import de.tum.in.www1.hephaestus.gitprovider.common.BaseGitServiceEntity; +import de.tum.in.www1.hephaestus.gitprovider.issuecomment.IssueComment; +import de.tum.in.www1.hephaestus.gitprovider.label.Label; +import de.tum.in.www1.hephaestus.gitprovider.milestone.Milestone; +import de.tum.in.www1.hephaestus.gitprovider.repository.Repository; +import de.tum.in.www1.hephaestus.gitprovider.user.User; + +@Entity +@Table(name = "issue") +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "issue_type", discriminatorType = DiscriminatorType.STRING) +@DiscriminatorValue(value = "ISSUE") +@Getter +@Setter +@NoArgsConstructor +@ToString(callSuper = true) +public class Issue extends BaseGitServiceEntity { + + private int number; + + @NonNull + @Enumerated(EnumType.STRING) + private Issue.State state; + + @NonNull + private String title; + + @Lob + private String body; + + @NonNull + private String htmlUrl; + + private boolean isLocked; + + private OffsetDateTime closedAt; + + private int commentsCount; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "author_id") + @ToString.Exclude + private User author; + + @ManyToMany + @JoinTable(name = "issue_label", joinColumns = @JoinColumn(name = "issue_id"), inverseJoinColumns = @JoinColumn(name = "label_id")) + @ToString.Exclude + private Set