Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
5d2eff8
openAI fix
hendrikebbers May 19, 2025
d43aabc
prompt as file
hendrikebbers May 19, 2025
1206a11
Update GitHub Code of Conduct provider configuration
danielmarv Jun 1, 2025
0f93651
Refactor GitHub integration: streamline Code of Conduct file handling…
danielmarv Jun 1, 2025
5edca0a
Add ViolationReport and ViolationReportRepository classes with CRUD o…
danielmarv Jun 1, 2025
0e34a74
Implement AnalysisService and ViolationReportService with analysis an…
danielmarv Jun 1, 2025
aabdc3a
Add DTO classes for analysis and violation reporting: AnalysisDto, Pa…
danielmarv Jun 1, 2025
45cad4e
Refactor DTOs to use records for AnalysisDto, PagedResponse, TrendAna…
danielmarv Jun 1, 2025
319d890
Implement ViolationReportController with endpoints for retrieving vio…
danielmarv Jun 1, 2025
b23f668
Update GitHub Code of Conduct provider configuration to use OpenEleme…
danielmarv Jun 1, 2025
e007b38
Refactor AnalysisDto and ViolationReportDto to use records; update An…
danielmarv Jul 4, 2025
424fa2e
Remove severity parameter from getReports method in ViolationReportCo…
danielmarv Jul 4, 2025
4e2d4d2
Remove severity filtering method from ViolationReportRepository; upda…
danielmarv Jul 4, 2025
aee988b
Update default page size to 100 in ViolationReportController for impr…
danielmarv Jul 4, 2025
3dafab6
Refactor pagination parameter validation in ViolationReportController…
danielmarv Jul 4, 2025
28048bf
Remove comment placeholder in getStats method of ViolationReportContr…
danielmarv Jul 4, 2025
0096310
`Removed TrendSummaryDto and related methods from AnalysisController …
danielmarv Jul 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/main/java/com/openelements/conduct/api/dto/AnalysisDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.openelements.conduct.api.dto;

import org.jspecify.annotations.NonNull;

import java.time.LocalDateTime;

public record AnalysisDto(
// Total counts
int totalNoViolationCount,
int totalPossibleViolationCount,
int totalViolationCount,

// Daily averages
int averageNoViolationCountPerDay,
int averagePossibleViolationCountPerDay,
int averageViolationCountPerDay,

// Daily maximums
int maxNoViolationCountPerDay,
int maxPossibleViolationCountPerDay,
int maxViolationCountPerDay,

// This week averages
int averageNoViolationCountPerDayInThisWeek,
int averagePossibleViolationCountPerDayInThisWeek,
int averageViolationCountPerDayInThisWeek,

// Growth metrics
double generalGrowthOfChecksInPercentage,
double growthOfNoViolationCountAgainstLastWeek,
double growthOfPossibleViolationCountAgainstLastWeek,
double growthOfViolationCountAgainstLastWeek,

@NonNull LocalDateTime analysisTimestamp
) {}
15 changes: 15 additions & 0 deletions src/main/java/com/openelements/conduct/api/dto/PagedResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.openelements.conduct.api.dto;

import org.jspecify.annotations.NonNull;

import java.util.List;

public record PagedResponse<T>(
@NonNull List<T> content,
int page,
int size,
long totalElements,
int totalPages,
boolean first,
boolean last
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.openelements.conduct.api.dto;

import org.jspecify.annotations.NonNull;

public record TrendSummaryDto(
@NonNull String trend,
double changePercentage,
@NonNull String description,
long totalReports,
double averageViolationsPerDay
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.openelements.conduct.api.dto;

import com.openelements.conduct.data.ViolationState;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

import java.net.URI;
import java.time.LocalDateTime;

public record ViolationReportDto(
@NonNull String id,
@Nullable String messageTitle,
@NonNull String messageContent,
@NonNull URI linkToViolation,
@NonNull ViolationState violationState,
@NonNull String reason,
@NonNull LocalDateTime timestamp
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.openelements.conduct.controller;

import com.openelements.conduct.api.dto.AnalysisDto;
import com.openelements.conduct.service.AnalysisService;
import org.jspecify.annotations.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;

@RestController
@RequestMapping("/api/v1/analysis")
public class AnalysisController {

private final AnalysisService analysisService;

@Autowired
public AnalysisController(@NonNull AnalysisService analysisService) {
this.analysisService = Objects.requireNonNull(analysisService, "analysisService must not be null");
}

@GetMapping
public AnalysisDto getAnalysis() {
return analysisService.generateAnalysis();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.openelements.conduct.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/v1/config")
public class ConfigurationController {

private final Environment environment;

@Autowired
public ConfigurationController(Environment environment) {
this.environment = environment;
}

@GetMapping
public ResponseEntity<Map<String, Object>> getConfiguration() {
Map<String, Object> config = new HashMap<>();

// Only expose non-sensitive configuration
config.put("application.name", environment.getProperty("spring.application.name", "Conduct Guardian"));
config.put("discord.enabled", environment.getProperty("guardian.integration.discord.enabled", "false"));
config.put("slack.enabled", environment.getProperty("guardian.integration.slack.enabled", "false"));
config.put("openai.enabled", environment.getProperty("guardian.integration.openai.enabled", "false"));
config.put("openai.model", environment.getProperty("guardian.integration.openai.model", "gpt-3.5-turbo"));
config.put("github.coc.enabled", environment.getProperty("guardian.integration.github.coc.enabled", "true"));
config.put("log.enabled", environment.getProperty("guardian.integration.log.enabled", "true"));

return ResponseEntity.ok(config);
}

@GetMapping("/integrations")
public ResponseEntity<IntegrationStatus> getIntegrationStatus() {
IntegrationStatus status = new IntegrationStatus(
Boolean.parseBoolean(environment.getProperty("guardian.integration.discord.enabled", "false")),
Boolean.parseBoolean(environment.getProperty("guardian.integration.slack.enabled", "false")),
Boolean.parseBoolean(environment.getProperty("guardian.integration.openai.enabled", "false")),
Boolean.parseBoolean(environment.getProperty("guardian.integration.github.coc.enabled", "true")),
Boolean.parseBoolean(environment.getProperty("guardian.integration.log.enabled", "true"))
);

return ResponseEntity.ok(status);
}

@PostMapping("/validate")
public ResponseEntity<ValidationResult> validateConfiguration(@RequestBody ConfigValidationRequest request) {
// Validate configuration without exposing sensitive data
boolean isValid = true;
String message = "Configuration is valid";

// Add validation logic here
if (request.checkOpenAI() && !hasOpenAIConfig()) {
isValid = false;
message = "OpenAI configuration is incomplete";
}

if (request.checkDiscord() && !hasDiscordConfig()) {
isValid = false;
message = "Discord configuration is incomplete";
}

return ResponseEntity.ok(new ValidationResult(isValid, message));
}

private boolean hasOpenAIConfig() {
return environment.getProperty("guardian.integration.openai.apiKey") != null &&
!environment.getProperty("guardian.integration.openai.apiKey", "").isEmpty();
}

private boolean hasDiscordConfig() {
return environment.getProperty("guardian.integration.discord.token") != null &&
!environment.getProperty("guardian.integration.discord.token", "").isEmpty();
}

public record IntegrationStatus(
boolean discordEnabled,
boolean slackEnabled,
boolean openaiEnabled,
boolean githubCocEnabled,
boolean logEnabled
) {}

public record ConfigValidationRequest(
boolean checkOpenAI,
boolean checkDiscord,
boolean checkSlack,
boolean checkGitHub
) {}

public record ValidationResult(
boolean isValid,
String message
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.openelements.conduct.controller;

import com.openelements.conduct.api.dto.PagedResponse;
import com.openelements.conduct.api.dto.ViolationReportDto;
import com.openelements.conduct.data.ViolationState;
import com.openelements.conduct.service.ViolationReportService;
import org.jspecify.annotations.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.Objects;

@RestController
@RequestMapping("/api/v1/violation-reports")
public class ViolationReportController {

private final ViolationReportService violationReportService;

@Autowired
public ViolationReportController(@NonNull ViolationReportService violationReportService) {
this.violationReportService = Objects.requireNonNull(violationReportService, "violationReportService must not be null");
}

@GetMapping
public ResponseEntity<PagedResponse<ViolationReportDto>> getReports(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(defaultValue = "timestamp") String sortBy,
@RequestParam(defaultValue = "desc") String sortDir,
@RequestParam(required = false) ViolationState violationState,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate) {

if (page < 0) page = 0;
if (size <= 0 || size > 100) size = 100;

PagedResponse<ViolationReportDto> response = violationReportService.getReports(
page, size, sortBy, sortDir, violationState, null, startDate, endDate
);

return ResponseEntity.ok(response);
}

@GetMapping("/{id}")
public ResponseEntity<ViolationReportDto> getReportById(@PathVariable String id) {
return violationReportService.getReportById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

@GetMapping("/stats")
public ResponseEntity<ReportStats> getStats() {
return ResponseEntity.ok(new ReportStats("Statistics endpoint - implement as needed"));
}

public record ReportStats(String message) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.openelements.conduct.integration.github;

import com.fasterxml.jackson.databind.JsonNode;

public interface GitHubClient {
JsonNode getRepositoryFileContent(String owner, String repo, String path, String branch) throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.openelements.conduct.integration.github;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class GitHubClientImpl implements GitHubClient {

private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();
private final String githubToken;

public GitHubClientImpl(@Value("${guardian.integration.github.coc.token:}") String githubToken) {
this.githubToken = githubToken;
}

@Override
public JsonNode getRepositoryFileContent(String owner, String repo, String path, String branch) throws Exception {
String url = String.format("https://api.github.com/repos/%s/%s/contents/%s?ref=%s", owner, repo, path, branch);

HttpHeaders headers = new HttpHeaders();
headers.set("Accept", "application/vnd.github.v3+json");
if (githubToken != null && !githubToken.isBlank()) {
headers.setBearerAuth(githubToken);
}

HttpEntity<Void> request = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, request, String.class);

if (response.getStatusCode().is2xxSuccessful()) {
return objectMapper.readTree(response.getBody());
}

throw new RuntimeException("Failed to fetch file from GitHub: " + response.getStatusCode());
}
}
Loading