Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,9 @@ public static void main(String[] args) {
graphDbPath = root.resolve(".osscodeiq/graph.db");
}

if (java.nio.file.Files.isDirectory(graphDbPath)) {
// Enriched Neo4j graph exists -- point Neo4j config to it
System.setProperty("codeiq.graph.path", graphDbPath.toString());
} else {
// No enriched graph -- Neo4j will start with an empty db,
// GraphBootstrapper will auto-load from H2 cache if available
System.setProperty("codeiq.graph.path", graphDbPath.toString());
}
// Point Neo4j config to the graph path (enriched or new empty db).
// GraphBootstrapper will auto-load from H2 cache if no enriched graph exists.
System.setProperty("codeiq.graph.path", graphDbPath.toString());
} else if (isIndex) {
app.setAdditionalProfiles("indexing");
// Index command: no web server, no Neo4j
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,10 @@ private String getGitHead(Path repoPath) {
if (exitCode == 0 && sha.length() >= 7) {
return sha;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.debug("Could not determine git HEAD", e);
return null;
} catch (Exception e) {
log.debug("Could not determine git HEAD", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@
int exitCode = process.waitFor();
process.getInputStream().close();
return exitCode == 0;
} catch (IOException | InterruptedException e) {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} catch (IOException e) {

Check warning on line 112 in src/main/java/io/github/randomcodespace/iq/analyzer/FileDiscovery.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "e" with an unnamed pattern.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1XQmbU8MsCUjGK9KmM&open=AZ1XQmbU8MsCUjGK9KmM&pullRequest=32
return false;
}
}
Expand Down Expand Up @@ -152,7 +155,11 @@
}
return result;

} catch (IOException | InterruptedException e) {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("git ls-files failed, falling back to filesystem walk", e);
return discoverViaWalk(root);
} catch (IOException e) {
log.warn("git ls-files failed, falling back to filesystem walk", e);
return discoverViaWalk(root);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
* REST API controller for service topology queries.
Expand All @@ -32,8 +33,8 @@ public class TopologyController {
private final TopologyService topologyService;
private final GraphStore graphStore;
private final CodeIqConfig config;
private volatile List<CodeNode> cachedNodes;
private volatile List<CodeEdge> cachedEdges;
private final AtomicReference<List<CodeNode>> cachedNodes = new AtomicReference<>();
private final AtomicReference<List<CodeEdge>> cachedEdges = new AtomicReference<>();

public TopologyController(TopologyService topologyService,
@Autowired(required = false) GraphStore graphStore,
Expand Down Expand Up @@ -65,15 +66,16 @@ private boolean hasNeo4jData() {
* Load data from Neo4j if available, otherwise from H2 cache.
*/
private synchronized void ensureDataLoaded() {
if (cachedNodes != null) return;
if (cachedNodes.get() != null) return;

// Try Neo4j first (has enriched data with SERVICE nodes)
if (hasNeo4jData()) {
cachedNodes = graphStore.findAll();
List<CodeNode> nodes = graphStore.findAll();
cachedNodes.set(nodes);
// Collect edges from all nodes' relationship lists
cachedEdges = cachedNodes.stream()
cachedEdges.set(nodes.stream()
.flatMap(n -> n.getEdges().stream())
.toList();
.toList());
return;
}

Expand All @@ -83,89 +85,91 @@ private synchronized void ensureDataLoaded() {
Path h2File = root.resolve(config.getCacheDir()).resolve("analysis-cache.mv.db");
if (!Files.exists(h2File)) return;
try (AnalysisCache cache = new AnalysisCache(cachePath)) {
cachedNodes = cache.loadAllNodes();
cachedEdges = cache.loadAllEdges();
cachedNodes.set(cache.loadAllNodes());
cachedEdges.set(cache.loadAllEdges());
}
}

/**
* Invalidate the in-memory cache (e.g. after re-analysis).
*/
public synchronized void invalidateCache() {
cachedNodes = null;
cachedEdges = null;
cachedNodes.set(null);
cachedEdges.set(null);
neo4jHasData = null;
}

@GetMapping
public Map<String, Object> getTopology() {
ensureDataLoaded();
requireCache();
return topologyService.getTopology(cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.getTopology(nodes, cachedEdges.get());
}

@GetMapping("/services/{name}")
public Map<String, Object> serviceDetail(@PathVariable String name) {
ensureDataLoaded();
requireCache();
return topologyService.serviceDetail(name, cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.serviceDetail(name, nodes, cachedEdges.get());
}

@GetMapping("/services/{name}/deps")
public Map<String, Object> serviceDependencies(@PathVariable String name) {
ensureDataLoaded();
requireCache();
return topologyService.serviceDependencies(name, cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.serviceDependencies(name, nodes, cachedEdges.get());
}

@GetMapping("/services/{name}/dependents")
public Map<String, Object> serviceDependents(@PathVariable String name) {
ensureDataLoaded();
requireCache();
return topologyService.serviceDependents(name, cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.serviceDependents(name, nodes, cachedEdges.get());
}

@GetMapping("/blast-radius/{nodeId}")
public Map<String, Object> blastRadius(@PathVariable String nodeId) {
ensureDataLoaded();
requireCache();
return topologyService.blastRadius(nodeId, cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.blastRadius(nodeId, nodes, cachedEdges.get());
}

@GetMapping("/path")
public List<Map<String, Object>> findPath(
@RequestParam("from") String source,
@RequestParam("to") String target) {
ensureDataLoaded();
requireCache();
return topologyService.findPath(source, target, cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.findPath(source, target, nodes, cachedEdges.get());
}

@GetMapping("/bottlenecks")
public List<Map<String, Object>> findBottlenecks() {
ensureDataLoaded();
requireCache();
return topologyService.findBottlenecks(cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.findBottlenecks(nodes, cachedEdges.get());
}

@GetMapping("/circular")
public List<List<String>> findCircularDeps() {
ensureDataLoaded();
requireCache();
return topologyService.findCircularDeps(cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.findCircularDeps(nodes, cachedEdges.get());
}

@GetMapping("/dead")
public List<Map<String, Object>> findDeadServices() {
ensureDataLoaded();
requireCache();
return topologyService.findDeadServices(cachedNodes, cachedEdges);
List<CodeNode> nodes = requireCache();
return topologyService.findDeadServices(nodes, cachedEdges.get());
}

private void requireCache() {
if (cachedNodes == null) {
private List<CodeNode> requireCache() {
List<CodeNode> nodes = cachedNodes.get();
if (nodes == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
"No analysis cache found. Run analyze first.");
}
return nodes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@
}
return count;
}
} catch (InterruptedException e) {

Check warning on line 385 in src/main/java/io/github/randomcodespace/iq/cli/BundleCommand.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "e" with an unnamed pattern.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1XQmfS8MsCUjGK9KmS&open=AZ1XQmfS8MsCUjGK9KmS&pullRequest=32
Thread.currentThread().interrupt();
} catch (Exception ignored) {
// Not a git repo or git not available
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
}

@Override
public DetectorResult detect(DetectorContext ctx) {

Check warning on line 47 in src/main/java/io/github/randomcodespace/iq/detector/config/GitHubActionsDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

A "Brain Method" was detected. Refactor it to reduce at least one of the following metrics: LOC from 89 to 64, Complexity from 18 to 14, Nesting Level from 3 to 2, Number of Variables from 33 to 6.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1XQmdd8MsCUjGK9KmO&open=AZ1XQmdd8MsCUjGK9KmO&pullRequest=32
if (!ctx.filePath().contains(".github/workflows/")) {
return DetectorResult.empty();
}
Expand Down Expand Up @@ -78,7 +78,12 @@
// YAML parses bare "on" as Boolean.TRUE
Object onTriggers = data.get("on");
if (onTriggers == null) {
onTriggers = data.get(Boolean.TRUE);
// SnakeYAML may parse bare 'on' key as Boolean.TRUE — search by entry value
onTriggers = data.entrySet().stream()
.filter(e -> Boolean.TRUE.equals(e.getKey()))

Check warning on line 83 in src/main/java/io/github/randomcodespace/iq/detector/config/GitHubActionsDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this call to "equals"; comparisons between unrelated types always return false.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1XQmdd8MsCUjGK9KmN&open=AZ1XQmdd8MsCUjGK9KmN&pullRequest=32
.map(java.util.Map.Entry::getValue)
.findFirst()
.orElse(null);
}
if (onTriggers == null) {
onTriggers = data.get("true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ protected DetectorResult detectWithRegex(DetectorContext ctx) {
String path = httpPath != null ? httpPath : "";
String fullPath;
if (finalClassRoute != null) {
fullPath = "/" + finalClassRoute.replaceAll("^/+|/+$", "");
fullPath = "/" + finalClassRoute.replaceAll("(^/+|/+$)", "");
if (!path.isEmpty()) fullPath = fullPath + "/" + path.replaceAll("^/+", "");
} else {
fullPath = !path.isEmpty() ? "/" + path.replaceAll("^/+", "") : "/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
private static final Pattern METHOD_RE = Pattern.compile(
"public\\s+(?:void|[\\w<>\\[\\]]+)\\s+(\\w+)\\s*\\(\\s*(\\w+)");
private static final Pattern GRPC_STUB_RE = Pattern.compile(
"(\\w+)Grpc\\.new(?:Blocking|Future|)Stub\\s*\\(");
"(\\w+)Grpc\\.new(?:Blocking|Future)?Stub\\s*\\(");

@Override
public String getName() {
Expand All @@ -48,7 +48,7 @@
}

@Override
public DetectorResult detect(DetectorContext ctx) {

Check warning on line 51 in src/main/java/io/github/randomcodespace/iq/detector/java/GrpcServiceDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

A "Brain Method" was detected. Refactor it to reduce at least one of the following metrics: LOC from 75 to 64, Complexity from 16 to 14, Nesting Level from 5 to 2, Number of Variables from 26 to 6.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1XQmeJ8MsCUjGK9KmQ&open=AZ1XQmeJ8MsCUjGK9KmQ&pullRequest=32
String text = ctx.content();
if (text == null || text.isEmpty()) return DetectorResult.empty();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
private static final Pattern DRIVER_MANAGER_RE = Pattern.compile(
"DriverManager\\s*\\.\\s*getConnection\\s*\\(\\s*\"(jdbc:[^\"]+)\"");
private static final Pattern JDBC_TEMPLATE_RE = Pattern.compile(
"(?:private|protected|public|final|\\s)+"
"(?:private|protected|public|final)\\s+"
+ "(?:final\\s+)?"
+ "(JdbcTemplate|NamedParameterJdbcTemplate|JdbcClient)"
+ "\\s+\\w+");
Expand All @@ -56,7 +56,7 @@
}

@Override
public DetectorResult detect(DetectorContext ctx) {

Check warning on line 59 in src/main/java/io/github/randomcodespace/iq/detector/java/JdbcDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

A "Brain Method" was detected. Refactor it to reduce at least one of the following metrics: LOC from 84 to 64, Complexity from 24 to 14, Nesting Level from 3 to 2, Number of Variables from 40 to 6.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1XQmej8MsCUjGK9KmR&open=AZ1XQmej8MsCUjGK9KmR&pullRequest=32
String text = ctx.content();
if (text == null || text.isEmpty()) {
return DetectorResult.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@
} else {
targetEntity = cit.getNameAsString();
}
break;
}
break;
}
}

Expand Down Expand Up @@ -229,7 +229,7 @@

// ==================== Regex fallback ====================

private DetectorResult detectWithRegex(DetectorContext ctx) {

Check warning on line 232 in src/main/java/io/github/randomcodespace/iq/detector/java/JpaEntityDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

A "Brain Method" was detected. Refactor it to reduce at least one of the following metrics: LOC from 85 to 64, Complexity from 19 to 14, Nesting Level from 5 to 2, Number of Variables from 34 to 6.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1XQmd68MsCUjGK9KmP&open=AZ1XQmd68MsCUjGK9KmP&pullRequest=32
String text = ctx.content();
String[] lines = text.split("\n", -1);
List<CodeNode> nodes = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ public class RawSqlDetector extends AbstractRegexDetector {

private static final Pattern CLASS_RE = Pattern.compile("(?:public\\s+)?class\\s+(\\w+)");
private static final Pattern QUERY_ANNO_RE = Pattern.compile(
"@Query\\s*\\(\\s*(?:value\\s*=\\s*)?\"((?:[^\"\\\\]|\\\\.)*)\"", Pattern.DOTALL);
"@Query\\s*\\(\\s*(?:value\\s*=\\s*)?\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"", Pattern.DOTALL);
private static final Pattern NATIVE_QUERY_RE = Pattern.compile("nativeQuery\\s*=\\s*true");
private static final Pattern JDBC_TEMPLATE_RE = Pattern.compile(
"(?:jdbcTemplate|namedParameterJdbcTemplate|JdbcTemplate)\\s*\\."
+ "(?:query|queryForObject|queryForList|queryForMap|update|execute|batchUpdate)"
+ "\\s*\\(\\s*\"((?:[^\"\\\\]|\\\\.)*)\"", Pattern.DOTALL);
+ "\\s*\\(\\s*\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"", Pattern.DOTALL);
private static final Pattern EM_QUERY_RE = Pattern.compile(
"(?:entityManager|em)\\s*\\.(?:createNativeQuery|createQuery)\\s*\\(\\s*\"((?:[^\"\\\\]|\\\\.)*)\"",
"(?:entityManager|em)\\s*\\.(?:createNativeQuery|createQuery)\\s*\\(\\s*\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"",
Pattern.DOTALL);
private static final Pattern TABLE_REF_RE = Pattern.compile(
"\\b(?:FROM|JOIN|INTO|UPDATE|TABLE)\\s+(\\w+)", Pattern.CASE_INSENSITIVE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ public class NestJSControllerDetector extends AbstractAntlrDetector {
private static final Pattern NESTJS_IMPORT = Pattern.compile("from\\s+['\"]@nestjs/");

private static final Pattern CONTROLLER_PATTERN = Pattern.compile(
"@Controller\\(\\s*['\"`]?([^'\"`\\)\\s]*)['\"`]?\\s*\\)(?:\\s*@\\w+\\([^)]*\\))*\\s*\\n\\s*(?:export\\s+)?class\\s+(\\w+)"
"@Controller\\(\\s*['\"`]?([^'\"`\\)\\s]*)['\"`]?\\s*\\)(?:\\s*@\\w+\\([^)]{0,200}\\))*\\s*\\n\\s*(?:export\\s+)?class\\s+(\\w+)"
);

private static final Pattern ROUTE_PATTERN = Pattern.compile(
"@(Get|Post|Put|Delete|Patch|Options|Head)\\(\\s*['\"`]?([^'\"`\\)\\s]*)['\"`]?\\s*\\)(?:\\s*@\\w+\\([^)]*\\))*\\s*\\n\\s*(?:async\\s+)?(\\w+)"
"@(Get|Post|Put|Delete|Patch|Options|Head)\\(\\s*['\"`]?([^'\"`\\)\\s]*)['\"`]?\\s*\\)(?:\\s*@\\w+\\([^)]{0,200}\\))*\\s*\\n\\s*(?:async\\s+)?(\\w+)"
);

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
} finally {
proc.destroy();
}
} catch (InterruptedException e) {

Check warning on line 51 in src/main/java/io/github/randomcodespace/iq/intelligence/RepositoryIdentity.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "e" with an unnamed pattern.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1XQmfl8MsCUjGK9KmT&open=AZ1XQmfl8MsCUjGK9KmT&pullRequest=32
Thread.currentThread().interrupt();
return null;
} catch (Exception e) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
* Catch-all controller that forwards unmatched routes to index.html
Expand Down Expand Up @@ -40,7 +41,7 @@ public String forward() {
* Does NOT match /api/**, /mcp/**, /actuator/**, or static assets.
*/
@GetMapping("/{path:[^\\.]*}")
public String catchAll() {
public String catchAll(@PathVariable String path) {
return "forward:/index.html";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ void shell_ormEntityMapping_isUnsupported() {
@Test
void unknownLanguage_allUnsupported() {
Map<CapabilityDimension, CapabilityLevel> caps = CapabilityMatrix.forLanguage("brainfuck");
assertThat(caps.values()).allMatch(l -> l == CapabilityLevel.UNSUPPORTED);
assertThat(caps.values()).isNotEmpty().allMatch(l -> l == CapabilityLevel.UNSUPPORTED);
}

@Test
void nullLanguage_allUnsupported() {
Map<CapabilityDimension, CapabilityLevel> caps = CapabilityMatrix.forLanguage(null);
assertThat(caps.values()).allMatch(l -> l == CapabilityLevel.UNSUPPORTED);
assertThat(caps.values()).isNotEmpty().allMatch(l -> l == CapabilityLevel.UNSUPPORTED);
}

@Test
Expand Down
Loading