-
Notifications
You must be signed in to change notification settings - Fork 49
Implement JSON export service for intelligence products (risk, coalition, trends) #8048
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
c5e35b8
e67d42f
7fbe114
7a307fa
5fc4262
ac4dbfe
82ed6cb
ab2cf9a
e50e9c6
1e75441
65ce937
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| /* | ||
| * Copyright 2010-2025 James Pether SΓΆrling | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| * | ||
| * $Id$ | ||
| * $HeadURL$ | ||
| */ | ||
| package com.hack23.cia.service.data.api; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| /** | ||
| * Service interface for exporting intelligence products as JSON. | ||
| * | ||
| * Provides JSON export capabilities for risk assessments, coalition alignment, | ||
| * and temporal trends to support CDN-ready static file generation and API endpoints. | ||
| * | ||
| * @author intelligence-operative | ||
| * @since v1.36 (Intelligence Products JSON Export) | ||
| */ | ||
| public interface IntelligenceExportService { | ||
|
|
||
| /** | ||
| * Export risk assessment data for all rule violations. | ||
| * | ||
| * Generates JSON containing all 50 risk rules with their current violations, | ||
| * severity levels, and affected entities (politicians, parties, committees, ministries). | ||
| * | ||
| * @return JSON string containing risk assessment data | ||
| * @throws IOException if JSON generation fails | ||
| */ | ||
| String exportRiskAssessments() throws IOException; | ||
|
|
||
| /** | ||
| * Export coalition alignment matrix showing voting alignment between parties. | ||
| * | ||
| * Generates JSON containing pairwise party alignment rates, total votes, | ||
| * and aligned votes for coalition stability analysis. | ||
| * | ||
| * @return JSON string containing coalition alignment matrix | ||
| * @throws IOException if JSON generation fails | ||
| */ | ||
| String exportCoalitionAlignment() throws IOException; | ||
|
|
||
| /** | ||
| * Export temporal trend analysis with daily/weekly/monthly/annual patterns. | ||
| * | ||
| * Generates JSON containing decision volumes, approval rates, moving averages, | ||
| * and year-over-year comparisons for trend forecasting. | ||
| * | ||
| * @return JSON string containing temporal trends data | ||
| * @throws IOException if JSON generation fails | ||
| */ | ||
| String exportTemporalTrends() throws IOException; | ||
|
|
||
| /** | ||
| * Write JSON export to file for CDN deployment. | ||
| * | ||
| * @param jsonContent the JSON content to write | ||
| * @param fileName the file name (e.g., "risk-assessments.json") | ||
| * @param outputDirectory the output directory path | ||
| * @throws IOException if file write fails | ||
| */ | ||
| void writeJsonToFile(String jsonContent, String fileName, String outputDirectory) throws IOException; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,181 @@ | ||||||||||
| /* | ||||||||||
| * Copyright 2010-2025 James Pether SΓΆrling | ||||||||||
| * | ||||||||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||
| * you may not use this file except in compliance with the License. | ||||||||||
| * You may obtain a copy of the License at | ||||||||||
| * | ||||||||||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||
| * | ||||||||||
| * Unless required by applicable law or agreed to in writing, software | ||||||||||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||
| * See the License for the specific language governing permissions and | ||||||||||
| * limitations under the License. | ||||||||||
| * | ||||||||||
| * $Id$ | ||||||||||
| * $HeadURL$ | ||||||||||
| */ | ||||||||||
| package com.hack23.cia.service.data.impl; | ||||||||||
|
|
||||||||||
| import java.io.File; | ||||||||||
| import java.io.IOException; | ||||||||||
| import java.nio.charset.StandardCharsets; | ||||||||||
| import java.nio.file.Files; | ||||||||||
| import java.nio.file.Paths; | ||||||||||
| import java.util.Date; | ||||||||||
| import java.util.List; | ||||||||||
|
|
||||||||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||||||||
| import org.springframework.stereotype.Service; | ||||||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||||||
|
|
||||||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||||||||
| import com.fasterxml.jackson.databind.SerializationFeature; | ||||||||||
| import com.hack23.cia.model.internal.application.data.impl.ViewDecisionTemporalTrends; | ||||||||||
| import com.hack23.cia.model.internal.application.data.party.impl.ViewRiksdagenCoalitionAlignmentMatrix; | ||||||||||
| import com.hack23.cia.model.internal.application.data.rules.impl.RuleViolation; | ||||||||||
| import com.hack23.cia.service.data.api.IntelligenceExportService; | ||||||||||
| import com.hack23.cia.service.data.api.RuleViolationDAO; | ||||||||||
| import com.hack23.cia.service.data.api.ViewDecisionTemporalTrendsDAO; | ||||||||||
| import com.hack23.cia.service.data.api.ViewRiksdagenCoalitionAlignmentMatrixDAO; | ||||||||||
| import com.hack23.cia.service.data.impl.export.CoalitionAlignmentExportDTO; | ||||||||||
| import com.hack23.cia.service.data.impl.export.CoalitionAlignmentExportDTO.PartyAlignment; | ||||||||||
| import com.hack23.cia.service.data.impl.export.ExportMetadata; | ||||||||||
| import com.hack23.cia.service.data.impl.export.RiskAssessmentExportDTO; | ||||||||||
| import com.hack23.cia.service.data.impl.export.RiskAssessmentExportDTO.RiskViolation; | ||||||||||
| import com.hack23.cia.service.data.impl.export.TemporalTrendsExportDTO; | ||||||||||
| import com.hack23.cia.service.data.impl.export.TemporalTrendsExportDTO.TrendDataPoint; | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Implementation of IntelligenceExportService. | ||||||||||
| * | ||||||||||
| * @author intelligence-operative | ||||||||||
| * @since v1.36 | ||||||||||
| */ | ||||||||||
| @Service | ||||||||||
| @Transactional(readOnly = true) | ||||||||||
| public class IntelligenceExportServiceImpl implements IntelligenceExportService { | ||||||||||
|
|
||||||||||
| @Autowired | ||||||||||
| private RuleViolationDAO ruleViolationDAO; | ||||||||||
|
|
||||||||||
| @Autowired | ||||||||||
| private ViewRiksdagenCoalitionAlignmentMatrixDAO coalitionAlignmentDAO; | ||||||||||
|
|
||||||||||
| @Autowired | ||||||||||
| private ViewDecisionTemporalTrendsDAO temporalTrendsDAO; | ||||||||||
|
|
||||||||||
| private final ObjectMapper objectMapper; | ||||||||||
|
|
||||||||||
| public IntelligenceExportServiceImpl() { | ||||||||||
| this.objectMapper = new ObjectMapper(); | ||||||||||
| this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); | ||||||||||
| this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); | ||||||||||
| } | ||||||||||
|
Comment on lines
73
to
78
|
||||||||||
|
|
||||||||||
| @Override | ||||||||||
| public String exportRiskAssessments() throws IOException { | ||||||||||
| final List<RuleViolation> violations = ruleViolationDAO.getAll(); | ||||||||||
|
|
||||||||||
| final RiskAssessmentExportDTO dto = new RiskAssessmentExportDTO(); | ||||||||||
|
|
||||||||||
| final ExportMetadata metadata = new ExportMetadata(); | ||||||||||
| metadata.setSchema("intelligence-schema"); | ||||||||||
|
Check failure on line 84 in service.data.impl/src/main/java/com/hack23/cia/service/data/impl/IntelligenceExportServiceImpl.java
|
||||||||||
| metadata.setRecordCount(violations.size()); | ||||||||||
| metadata.setDataDate(new Date()); | ||||||||||
| dto.setMetadata(metadata); | ||||||||||
|
|
||||||||||
| for (final com.hack23.cia.model.internal.application.data.rules.impl.RuleViolation violation : violations) { | ||||||||||
|
||||||||||
| for (final com.hack23.cia.model.internal.application.data.rules.impl.RuleViolation violation : violations) { | |
| for (final RuleViolation violation : violations) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 5fc4262. Removed fully-qualified class name and used the imported RuleViolation directly.
Outdated
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of mkdirs() without checking the return value can lead to silent failures. If directory creation fails (e.g., due to permissions), the subsequent Files.write() will fail. Check the return value and throw an appropriate exception:
if (!directory.exists() && !directory.mkdirs()) {
throw new IOException("Failed to create directory: " + outputDirectory);
}| if (!directory.exists()) { | |
| directory.mkdirs(); | |
| if (!directory.exists() && !directory.mkdirs()) { | |
| throw new IOException("Failed to create directory: " + outputDirectory); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 5fc4262. Added check for mkdirs() return value and throw IOException with descriptive message if directory creation fails.
Outdated
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The writeJsonToFile method is vulnerable to path traversal attacks. An attacker could provide a malicious fileName containing path traversal sequences (e.g., ../../etc/passwd) to write files outside the intended directory. Validate and sanitize the fileName parameter to ensure it doesn't contain directory separators or path traversal sequences:
// Validate fileName doesn't contain path separators
if (fileName.contains("/") || fileName.contains("\\") || fileName.contains("..")) {
throw new IllegalArgumentException("Invalid file name: must not contain path separators or traversal sequences");
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 5fc4262. Added validation to reject file names containing /, \, or .. to prevent path traversal attacks. Throws IllegalArgumentException with descriptive message.
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The writeJsonToFile method is not covered by unit tests. Add a test case to verify file creation, directory creation, and content writing behavior. Consider testing both scenarios: when the directory exists and when it needs to be created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 5fc4262. Added 3 new tests for writeJsonToFile: testWriteJsonToFile (basic functionality), testWriteJsonToFileCreatesDirectory (directory creation), and testWriteJsonToFileRejectsPathTraversal (security validation).
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,129 @@ | ||||||
| /* | ||||||
| * Copyright 2010-2025 James Pether SΓΆrling | ||||||
| * | ||||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| * you may not use this file except in compliance with the License. | ||||||
| * You may obtain a copy of the License at | ||||||
| * | ||||||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
| * | ||||||
| * Unless required by applicable law or agreed to in writing, software | ||||||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
| * See the License for the specific language governing permissions and | ||||||
| * limitations under the License. | ||||||
| * | ||||||
| * $Id$ | ||||||
| * $HeadURL$ | ||||||
| */ | ||||||
| package com.hack23.cia.service.data.impl.export; | ||||||
|
|
||||||
| import java.io.Serializable; | ||||||
| import java.math.BigDecimal; | ||||||
| import java.util.ArrayList; | ||||||
| import java.util.List; | ||||||
|
|
||||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||||
|
|
||||||
| /** | ||||||
| * DTO for coalition alignment JSON export. | ||||||
| * | ||||||
| * @author intelligence-operative | ||||||
| * @since v1.36 | ||||||
| */ | ||||||
| @JsonInclude(JsonInclude.Include.NON_NULL) | ||||||
| public class CoalitionAlignmentExportDTO implements Serializable { | ||||||
|
|
||||||
| private static final long serialVersionUID = 1L; | ||||||
|
|
||||||
| @JsonProperty("metadata") | ||||||
| private ExportMetadata metadata; | ||||||
|
|
||||||
| @JsonProperty("alignments") | ||||||
| private List<PartyAlignment> alignments; | ||||||
|
|
||||||
| public CoalitionAlignmentExportDTO() { | ||||||
| this.alignments = new ArrayList<>(); | ||||||
| } | ||||||
|
|
||||||
| public ExportMetadata getMetadata() { | ||||||
| return metadata; | ||||||
| } | ||||||
|
|
||||||
| public void setMetadata(final ExportMetadata metadata) { | ||||||
| this.metadata = metadata; | ||||||
| } | ||||||
|
|
||||||
| public List<PartyAlignment> getAlignments() { | ||||||
github-code-quality[bot] marked this conversation as resolved.
Fixed
Show fixed
Hide fixed
|
||||||
| return alignments; | ||||||
|
||||||
| return alignments; | |
| return List.copyOf(alignments); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These DTOs are specifically designed for Jackson JSON serialization where the service implementation populates the lists via dto.getAlignments().add(). Using List.copyOf() would return an immutable list causing UnsupportedOperationException. The current implementation is appropriate for DTOs used in serialization contexts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class is declared as
public classwhich breaks the codebase convention. All other service implementations in this package usefinal classwith package-private visibility (no public modifier). This should be:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 5fc4262. Changed from
public classtofinal classto match codebase conventions.