All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cthul.quarkus.dynamicmessages.cockpit.CockpitLocator Maven / Gradle / Ivy

The newest version!
package org.cthul.quarkus.dynamicmessages.cockpit;

import io.quarkus.logging.Log;
import io.quarkus.qute.TemplateLocator;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.cthul.quarkus.dynamicmessages.*;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;

@ApplicationScoped
public class CockpitLocator implements DynamicTemplateLocator {

    @Inject
    CockpitConfig config;

    @Inject
    @RestClient
    CockpitClient client;

    @Inject
    DynamicTemplates dynamicTemplates;

    private MessageBackup backup = null;
    private final KeyRename bundles = new KeyRename();
    private final KeyRename templates = new KeyRename('_', '.');
    private final Set projects = new HashSet<>();
    private final Map>> projectLocalizations = new HashMap<>();
    private long lastRefresh = 0;

    @PostConstruct
    void init() {
        config.backupFile().ifPresent(file -> {
            backup = new MessageBackup(Path.of(file));
            projectLocalizations.putAll(backup.read());
        });
        config.mappings().forEach((name, mapping) -> {
            var project = mapping.project().orElse(name);
            if (project.equals(CockpitConfig.DEFAULT_MAPPING)) return;
            projects.add(project);
            var prefix = mapping.prefix().orElse("");
            var bundle = mapping.bundle().orElse(name);
            if (!bundle.equals(CockpitConfig.DEFAULT_MAPPING)) bundles.addBundlePrefix(bundle, project, prefix);
            var path = mapping.path().orElse(name);
            if (!path.equals(CockpitConfig.DEFAULT_MAPPING)) templates.addBundlePrefix(path, project, prefix);
        });
        autoRefresh();
    }

    @Override
    public void initialize(Set messageIds) {
        if (autoRefresh()) return;
        var matches = messageIds.stream()
                .filter(id -> locate(bundles.mapMessage(id)) != null)
                .collect(Collectors.toSet());
        dynamicTemplates.resetAll(matches);
    }

    public boolean autoRefresh() {
        if (System.currentTimeMillis() - lastRefresh > 15 * 60 * 1000) {
            return refresh();
        }
        return true;
    }

    public boolean refresh() {
        try {
            var reset = new HashSet();
            projects.forEach(project -> {
                var data = client.lokalize(project, config.token().orElse(""));
                refresh(project, data.getTranslations(), reset);
            });
            Log.debugf("Refreshed localizations: %s -> %s", projects, reset);
            lastRefresh = System.currentTimeMillis();
            if (reset.isEmpty()) return false;
            if (backup != null) {
                backup.write(projectLocalizations);
            }
            dynamicTemplates.resetAll(reset);
            return true;
        } catch (Exception e) {
            Log.error("Failed to refresh localizations", e);
            return false;
        }
    }

    private synchronized void refresh(String project, Map> data, Set reset) {
        var existing = projectLocalizations.computeIfAbsent(project, p -> new HashMap<>());
        data.forEach((lang, translations) -> {
            var existingLang = existing.computeIfAbsent(lang, l -> new HashMap<>());
            translations.forEach((key, value) -> {
                var existingValue = existingLang.put(key, value);
                if (!Objects.equals(existingValue, value)) {
                    var bundleId = bundles.mapKey(lang, project, key);
                    if (bundleId != null) {
                        reset.add(bundleId.messageId());
                        reset.add(bundleId.messageId("en"));
                    }
                    var templateId = templates.mapKey(lang, project, key);
                    if (templateId != null) {
                        reset.add(templateId.messageId());
                        reset.add(templateId.messageId("en"));
                    }
                }
            });
        });
    }

    @Override
    public Optional locate(String id) {
        return Optional.ofNullable(templates.mapMessage(id))
                .or(() -> Optional.ofNullable(bundles.mapMessage(id)))
                .map(this::locate)
                .map(StringTemplateLocation::new);
    }

    private String locate(KeyRename.MappedKey mapped) {
        if (mapped == null) return null;
        return get(mapped.namespace(), mapped.locale(), mapped.key());
    }

    public synchronized String get(String project, String locale, String key) {
        autoRefresh();
        if (locale.isBlank()) locale = "default";
        var projectData = projectLocalizations.get(project);
        if (projectData == null) return null;
        var result = get(projectData, locale, key);
        if (result != null) {
            Log.debugf("Found localization: %s:%s:%s", project, locale, key);
            return result;
        }
        if (!locale.equals("default") && get(projectData, "default", key) != null) {
            Log.debugf("Found default localization: %s:%s:%s", project, locale, key);
            var ref = templates.mapKey("default", project, key);
            return "{#include %s}".formatted(ref.messageId("default"));
        }
        Log.debugf("Localization not found: %s:%s:%s", project, locale, key);
        return null;
    }

    private static String get(Map> projectData, String locale, String key) {
        var translations = projectData.get(locale);
        if (translations != null) {
            var result = translations.get(key);
            if (result != null && !result.isEmpty()) {
                return result;
            }
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy