Skip to content

Commit

Permalink
Merge pull request #26 from ruromero/redis-multi-key
Browse files Browse the repository at this point in the history
feat: remove dependency with redis search
  • Loading branch information
ruromero authored Jan 30, 2024
2 parents 53f9f99 + 96efabb commit 195d53c
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 158 deletions.
35 changes: 35 additions & 0 deletions src/main/java/com/redhat/ecosystemappeng/onguard/model/Alias.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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.
*/
package com.redhat.ecosystemappeng.onguard.model;

import io.quarkus.runtime.annotations.RegisterForReflection;

@RegisterForReflection
public record Alias(String id, String cveId) {

public static String getKey(String id) {
return "alias:" + id;
}

public static String getKey(Alias alias) {
if(alias == null) {
return null;
}
return getKey(alias.id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import com.redhat.ecosystemappeng.onguard.model.osv.Affected;

public record Vulnerability(
List<String> aliases,
String cveId,
Date created,
Date lastModified,
Expand All @@ -49,11 +48,10 @@ public static class Builder {
List<Affected> affected;
Metrics metrics;

public Builder() {
private Builder() {
}

public Builder(Vulnerability other) {
this.aliases = other.aliases;
private Builder(Vulnerability other) {
this.cveId = other.cveId;
this.created = other.created;
this.lastModified = other.lastModified;
Expand All @@ -63,11 +61,6 @@ public Builder(Vulnerability other) {
this.metrics = other.metrics;
}

public Builder aliases(List<String> aliases) {
this.aliases = aliases;
return this;
}

public Builder cveId(String cveId) {
this.cveId = cveId;
return this;
Expand Down Expand Up @@ -106,10 +99,6 @@ public Builder metrics(Metrics metrics) {
public String getCveId() {
return cveId;
}

public List<String> getAliases() {
return aliases;
}

public Vulnerability build() {
if(cveId == null && aliases != null) {
Expand All @@ -118,9 +107,15 @@ public Vulnerability build() {
cveId = alias.get();
}
}
return new Vulnerability(aliases, cveId, created, lastModified, summary, description, affected, metrics);
return new Vulnerability(cveId, created, lastModified, summary, description, affected, metrics);
}
}

public static Builder builder() {
return new Builder();
}

public static Builder builder(Vulnerability other) {
return new Builder(other);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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.
*/
package com.redhat.ecosystemappeng.onguard.model;

import io.quarkus.runtime.annotations.RegisterForReflection;

@RegisterForReflection
public record VulnerabilityAlias(String alias, Vulnerability vulnerability) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;

import com.redhat.ecosystemappeng.onguard.model.Vulnerability;
import com.redhat.ecosystemappeng.onguard.model.VulnerabilityAlias;

public interface VulnerabilityRepository {

Expand All @@ -29,7 +30,9 @@ public interface VulnerabilityRepository {

List<Vulnerability> list(List<String> cves);

Vulnerability getByAlias(String alias);
VulnerabilityAlias getByAlias(String alias);

List<Vulnerability> listByAliases(List<String> aliases);
List<VulnerabilityAlias> listByAliases(List<String> aliases);

void setAliases(List<String> aliases, String cveId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,74 +17,67 @@
*/
package com.redhat.ecosystemappeng.onguard.repository.redis;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletionException;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.redhat.ecosystemappeng.onguard.model.Alias;
import com.redhat.ecosystemappeng.onguard.model.Vulnerability;
import com.redhat.ecosystemappeng.onguard.model.VulnerabilityAlias;
import com.redhat.ecosystemappeng.onguard.repository.VulnerabilityRepository;

import io.quarkus.redis.datasource.RedisDataSource;
import io.quarkus.redis.datasource.json.JsonCommands;
import io.quarkus.redis.datasource.search.CreateArgs;
import io.quarkus.redis.datasource.search.FieldType;
import io.quarkus.redis.datasource.search.QueryArgs;
import io.quarkus.redis.datasource.search.SearchCommands;
import io.quarkus.redis.datasource.value.ValueCommands;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
public class VulnerabilityRedisRepository implements VulnerabilityRepository {

private static final String VULNID_INDEX = "idx:aliases";

private static final Logger LOGGER = LoggerFactory.getLogger(VulnerabilityRedisRepository.class);

@Inject
ObjectMapper mapper;

private final ValueCommands<String, Alias> aliasCommands;
private final JsonCommands<String> jsonCommands;
private final SearchCommands<String> searchCommands;

public VulnerabilityRedisRepository(RedisDataSource ds) {
this.jsonCommands = ds.json(String.class);
this.searchCommands = ds.search();
try {
// Requires creating an index
// FT.CREATE idx:aliases ON JSON SCHEMA $.aliases as aliases TAG
if (this.searchCommands.ft_list().stream().noneMatch(s -> s.equals(VULNID_INDEX))) {
this.searchCommands.ftCreate(VULNID_INDEX,
new CreateArgs()
.indexedField("$.aliases", "aliases", FieldType.TAG)
.onJson());
}
} catch (CompletionException e) {
LOGGER.warn("Trying to create existing index {}", VULNID_INDEX, e);
}
this.aliasCommands = ds.value(Alias.class);
}

@Override
public Vulnerability get(String cveId) {
if (cveId == null) {
return null;
}
var vuln = jsonCommands.jsonGet(cveId, Vulnerability.class);
if (vuln == null) {
return new Vulnerability.Builder().cveId(cveId).build();
return Vulnerability.builder().cveId(cveId).build();
}
return vuln;
}

@Override
public void save(Vulnerability vulnerability) {
jsonCommands.jsonSet(vulnerability.cveId(), vulnerability);
var alias = new Alias(vulnerability.cveId(), vulnerability.cveId());
aliasCommands.set(Alias.getKey(alias.id()), alias);
}

public void setAliases(List<String> aliases, String cveId) {
aliasCommands.mset(aliases.stream().collect(Collectors.toMap(v -> Alias.getKey(v), v -> new Alias(v, cveId))));
}

//TODO: Can work with mget?
@Override
public List<Vulnerability> list(List<String> cves) {
// Arguments do not match the signature...
Expand All @@ -106,59 +99,25 @@ public List<Vulnerability> list(List<String> cves) {
}
}
if (vuln == null) {
vuln = new Vulnerability.Builder().cveId(cves.get(i)).build();
vuln = Vulnerability.builder().cveId(cves.get(i)).build();
}
results.add(vuln);
}
return results;
}

@Override
public Vulnerability getByAlias(String alias) {
var response = searchCommands.ftSearch(VULNID_INDEX, "@aliases:{" + alias.replaceAll("\\-", "\\\\-") + "}",
new QueryArgs().limit(0, 1).withPayloads());
if (response != null && !response.documents().isEmpty()) {
try {
return mapper.readValue(response.documents().get(0).property("$").asBytes(), Vulnerability.class);
} catch (IOException e) {
LOGGER.error("Unable to deserialize Vulnerability {}", alias, e);
}
public VulnerabilityAlias getByAlias(String aliasId) {
var alias = aliasCommands.get(Alias.getKey(aliasId));
if(alias == null) {
return new VulnerabilityAlias(aliasId, null);
}
return new Vulnerability.Builder().aliases(List.of(alias)).build();
return new VulnerabilityAlias(aliasId, get(alias.cveId()));
}

@Override
public List<Vulnerability> listByAliases(List<String> aliases) {
var response = searchCommands.ftSearch(VULNID_INDEX,
"@aliases:{" + String.join("|", aliases.toArray(new String[0])).replaceAll("\\-", "\\\\-") + "}",
new QueryArgs().verbatim().withPayloads());
if (response == null || response.documents().isEmpty()) {
return aliases.stream().map(alias -> new Vulnerability.Builder().aliases(List.of(alias)).build()).toList();
}
var found = response.documents().stream().map(v -> {
try {
var val = mapper.readValue(v.property("$").asBytes(), Vulnerability.class);
return val;
} catch (IOException e) {
String alias = v.property("aliases").asString();
LOGGER.error("Unable to deserialize Vulnerability {}", alias, e);
return new Vulnerability.Builder().aliases(List.of(alias)).build();
}
}).collect(Collectors.toMap(val -> {
var alias = aliases.stream().filter(a -> val.aliases().contains(a)).findFirst();
if (alias.isPresent()) {
return alias.get();
}
throw new IllegalStateException("Unexpected item received: " + val.aliases());
}, val -> val));

return aliases.stream().map(alias -> {
Vulnerability vuln = found.get(alias);
if (vuln == null) {
return new Vulnerability.Builder().aliases(List.of(alias)).build();
}
return vuln;
}).toList();
public List<VulnerabilityAlias> listByAliases(List<String> aliasIds) {
return aliasIds.stream().parallel().map(this::getByAlias).toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,14 @@ public void sync() {
var when = bulkRepository.get();
if(when == null) {
LOGGER.info("Skipping scheduled sync because the database has not been pre-loaded");
} if (when.completed() == null) {
return;
}
if (when.completed() == null) {
LOGGER.info("Waiting for current migration to complete");
} else {
LOGGER.info("Started scheduled sync since: {}", when.completed());
when = bulkRepository.remove();
loadFromNvdApi(when.completed());
return;
}
LOGGER.info("Started scheduled sync since: {}", when.completed());
when = bulkRepository.remove();
loadFromNvdApi(when.completed());
}
}
Loading

0 comments on commit 195d53c

Please sign in to comment.