Tagger.java
package jasper.component;
import io.micrometer.core.annotation.Timed;
import jasper.domain.Ref;
import jasper.domain.proj.Tag;
import jasper.errors.AlreadyExistsException;
import jasper.errors.InvalidPluginException;
import jasper.errors.ModifiedException;
import jasper.repository.RefRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static jasper.domain.Ref.from;
import static jasper.domain.proj.Tag.capturesDownwards;
import static jasper.domain.proj.Tag.urlForTag;
import static java.time.Instant.now;
import static java.util.Arrays.asList;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Service
public class Tagger {
private static final Logger logger = LoggerFactory.getLogger(Tagger.class);
@Autowired
ConfigCache configs;
@Autowired
RefRepository refRepository;
@Autowired
Ingest ingest;
@Timed(value = "jasper.tagger", histogram = true)
public Ref internalTag(String url, String origin, String ...tags) {
return tag(true, true, url, origin, tags);
}
@Timed(value = "jasper.tagger", histogram = true)
public Ref tag(String url, String origin, String ...tags) {
return tag(true, false, url, origin, tags);
}
public Ref tag(boolean retry, boolean internal, String url, String origin, String ...tags) {
var maybeRef = refRepository.findOneByUrlAndOrigin(url, origin);
if (configs.getRemote(origin) != null) return maybeRef.orElse(null);
if (maybeRef.isEmpty()) {
var ref = from(url, origin, tags);
if (internal) ref.addTag("internal");
try {
ingest.create(origin, ref);
} catch (AlreadyExistsException e) {
return tag(retry, internal, url, origin, tags);
}
return ref;
} else {
var ref = maybeRef.get();
if (ref.hasTag(tags)) return ref;
ref.addTags(asList(tags));
try {
ingest.update(origin, ref);
} catch (InvalidPluginException e) {
ingest.silent(origin, ref);
} catch (ModifiedException e) {
// TODO: infinite retrys?
if (retry) return tag(true, internal, url, origin, tags);
return null;
}
return ref;
}
}
public Ref remove(String url, String origin, String ...tags) {
var maybeRef = refRepository.findOneByUrlAndOrigin(url, origin);
if (configs.getRemote(origin) != null) return maybeRef.orElse(null);
if (maybeRef.isEmpty()) return null;
var ref = maybeRef.get();
if (!ref.hasTag(tags)) return ref;
ref.removeTags(asList(tags));
try {
ingest.update(origin, ref);
} catch (ModifiedException e) {
// TODO: infinite retrys?
return remove(url, origin, tags);
}
return ref;
}
@Timed(value = "jasper.tagger", histogram = true)
public Ref plugin(String url, String origin, String tag, Object plugin, String ...tags) {
if (configs.getRemote(origin) != null) return silentPlugin(url, "", origin, tag, plugin, tags);
return plugin(true, url, origin, null, tag, plugin, tags);
}
@Timed(value = "jasper.tagger", histogram = true)
public Ref newPlugin(String url, String title, String origin, String tag, Object plugin, String ...tags) {
if (configs.getRemote(origin) != null) return silentPlugin(url, title, origin, tag, plugin, tags);
return plugin(true, url, origin, title, tag, plugin, tags);
}
/**
* For monkey patching replicated origins.
*/
Ref silentPlugin(String url, String title, String origin, String tag, Object plugin, String ...tags) {
var maybeRef = refRepository.findOneByUrlAndOrigin(url, origin);
if (maybeRef.isEmpty()) {
var ref = from(url, origin, tags).setPlugin(tag, plugin);
ref.setTitle(title);
ref.addTag("internal");
var cursor = refRepository.getCursor(origin);
if (cursor == null) {
logger.warn("Silent plugin can't be first!");
ref.setModified(now().minusMillis(1));
} else {
ref.setModified(cursor.minusMillis(1));
}
ingest.silent(origin, ref);
return ref;
} else {
var ref = maybeRef.get();
// TODO: check if plugin already matches exactly and skip
ref.setPlugin(tag, plugin);
ref.addTags(asList(tags));
ingest.silent(origin, ref);
return ref;
}
}
Ref plugin(boolean retry, String url, String origin, String title, String tag, Object plugin, String ...tags) {
var maybeRef = refRepository.findOneByUrlAndOrigin(url, origin);
if (configs.getRemote(origin) != null) return maybeRef.orElse(null);
if (maybeRef.isEmpty()) {
var ref = from(url, origin, tags).setPlugin(tag, plugin);
ref.setTitle(title);
ref.addTag("internal");
try {
ingest.create(origin, ref);
} catch (AlreadyExistsException e) {
return plugin(retry, url, origin, title, tag, plugin, tags);
}
return ref;
} else {
var ref = maybeRef.get();
// TODO: check if plugin already matches exactly and skip
ref.setPlugin(tag, plugin);
ref.addTags(asList(tags));
try {
ingest.update(origin, ref);
} catch (ModifiedException e) {
// TODO: infinite retrys?
if (retry) return plugin(retry, url, origin, title, tag, plugin, tags);
return null;
}
return ref;
}
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void attachLogs(String url, String origin, String msg) {
attachLogs(origin, tag(url, origin), "", msg);
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void attachLogs(String url, String origin, String title, String logs) {
attachLogs(origin, tag(url, origin), title, logs);
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void attachLogs(String origin, Ref parent, String msg) {
attachLogs(origin, parent, "", msg);
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void attachLogs(String origin, Ref parent, String title, String logs) {
var ref = new Ref();
ref.setOrigin(origin);
ref.setUrl("error:" + UUID.randomUUID());
ref.setSources(List.of(parent.getUrl()));
ref.setTitle(title);
ref.setComment(logs);
var tags = new ArrayList<>(List.of("internal", "+plugin/log"));
if (parent.hasTag("public")) tags.add("public");
if (origin.equals(parent.getOrigin()) && parent.getTags() != null) {
tags.addAll(parent.getTags().stream().filter(t -> capturesDownwards("_user", t)).map(Tag::publicTag).toList());
}
ref.setTags(tags);
ingest.create(origin, ref);
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void attachError(String url, String origin, String msg) {
attachError(origin, tag(url, origin, "+plugin/error"), "", msg);
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void attachError(String url, String origin, String title, String logs) {
attachError(origin, tag(url, origin, "+plugin/error"), title, logs);
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void attachError(String origin, Ref parent, String msg) {
attachError(origin, parent, "", msg);
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void attachError(String origin, Ref parent, String title, String logs) {
var remote = configs.getRemote(origin);
if (remote != null) origin = remote.getOrigin();
attachLogs(origin, parent, title, logs);
if (remote == null && !parent.hasTag("+plugin/error")) {
tag(false, true, parent.getUrl(), parent.getOrigin(), "+plugin/error");
}
}
@Timed(value = "jasper.tagger", histogram = true)
public Ref getResponseRef(String user, String origin, String url) {
var userUrl = urlForTag(url, user);
return refRepository.findOneByUrlAndOrigin(userUrl, origin).map(ref -> {
if (isNotBlank(url) && (ref.getSources() == null || !ref.getSources().contains(url))) {
ref.setSources(new ArrayList<>(List.of(url)));
}
if (ref.getTags() == null || ref.hasTag("plugin/deleted")) {
ref.setTags(new ArrayList<>(List.of("internal", user)));
}
return ref;
})
.orElseGet(() -> {
var ref = new Ref();
ref.setUrl(userUrl);
ref.setOrigin(origin);
if (isNotBlank(url)) ref.setSources(new ArrayList<>(List.of(url)));
ref.setTags(new ArrayList<>(List.of("internal", user)));
ingest.create(origin, ref);
return ref;
});
}
@Async
@Timed(value = "jasper.tagger", histogram = true)
public void removeAllResponses(String url, String origin, String tag) {
var remote = configs.getRemote(origin);
if (remote != null) origin = remote.getOrigin();
for (var res : refRepository.findAllResponsesWithTag(url, origin, tag)) {
internalTag(res, origin, "-" + tag);
}
}
}