From 5c6ef91b43f0b0cc225b1a5b8abdc08b670a9317 Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Sun, 09 Dec 2018 23:01:56 +0000
Subject: [PATCH] ...

---
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractCache.java         |   14 ++
 borgbutler-webapp/src/components/views/repos/RepoListView.css                          |   44 +++++++
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveListCache.java      |    6 
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoListCache.java         |    6 
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractElementsCache.java |    6 
 borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java             |    1 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java     |   58 +++++++++
 borgbutler-webapp/src/components/views/repos/RepoCard.jsx                              |   43 +++++++
 borgbutler-core/src/main/java/de/micromata/borgbutler/config/ConfigurationHandler.java |    3 
 borgbutler-webapp/src/components/views/develop/RestServices.jsx                        |    6 +
 borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java    |    1 
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java           |   25 ++++
 borgbutler-webapp/src/components/views/repos/RepoListView.jsx                          |  125 ++++++++++++++++++++
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoInfoCache.java         |    6 
 14 files changed, 329 insertions(+), 15 deletions(-)

diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractCache.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractCache.java
index 7cb2908..d89de91 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractCache.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractCache.java
@@ -46,6 +46,20 @@
         state = STATE.DIRTY;
     }
 
+    /**
+     * Calls {@link #clear()} and removes the cache files. Therefore a new creation of this cache is forced.
+     */
+    public void clearAndReset() {
+        synchronized (this) {
+            if (cacheFile.exists()) {
+                log.info("Clearing cache and deleting cache file (recreation is forced): " + cacheFile.getAbsolutePath());
+                cacheFile.delete();
+            }
+            clear();
+            state = STATE.SAVED;
+        }
+    }
+
     protected void setDirty() {
         state = STATE.DIRTY;
     }
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractElementsCache.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractElementsCache.java
index c7e5ad4..67c2604 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractElementsCache.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractElementsCache.java
@@ -32,11 +32,11 @@
 
     protected abstract T load(BorgRepoConfig repoConfig, String identifier);
 
-    public abstract boolean matches(T element, String identifier);
+    protected abstract boolean matches(T element, String identifier);
 
-    public abstract String getIdentifier(T element);
+    protected abstract String getIdentifier(T element);
 
-    public abstract void updateFrom(T dest, T source);
+    protected abstract void updateFrom(T dest, T source);
 
     /**
      * Removes all entries (doesn't effect the cache files!).
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveListCache.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveListCache.java
index 3f31c3c..a2ec7d9 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveListCache.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveListCache.java
@@ -20,17 +20,17 @@
     }
 
     @Override
-    public boolean matches(ArchiveInfo element, String identifier) {
+    protected boolean matches(ArchiveInfo element, String identifier) {
         return element.matches(identifier);
     }
 
     @Override
-    public String getIdentifier(ArchiveInfo element) {
+    protected String getIdentifier(ArchiveInfo element) {
         return element.getRepository().getId();
     }
 
     @Override
-    public void updateFrom(ArchiveInfo dest, ArchiveInfo source) {
+    protected void updateFrom(ArchiveInfo dest, ArchiveInfo source) {
         dest.updateFrom(source);
     }
 
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
index 00ecb3b..946b81d 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
@@ -5,6 +5,8 @@
 import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.json.borg.Archive;
 import de.micromata.borgbutler.json.borg.FilesystemItem;
+import de.micromata.borgbutler.json.borg.RepoInfo;
+import de.micromata.borgbutler.json.borg.Repository;
 import lombok.Getter;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -40,6 +42,29 @@
         }
     }
 
+    public Repository getRepository(String idOrName) {
+        BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(idOrName);
+        RepoInfo repoInfo = repoInfoCache.get(repoConfig, idOrName);
+        if (repoInfo == null) {
+            log.warn("Repo with name or id '" + idOrName + "' not found.");
+            return null;
+        }
+        return repoInfo.getRepository();
+    }
+
+    public List<Repository> getAllRepositories() {
+        List<Repository> repositories = new ArrayList<>();
+        for (BorgRepoConfig repoConfig : ConfigurationHandler.getConfiguration().getRepoConfigs()) {
+            RepoInfo repoInfo = repoInfoCache.get(repoConfig, repoConfig.getName());
+            if (repoInfo == null) {
+                log.warn("Repo with name '" + repoConfig.getName() + "' not found.");
+                continue;
+            }
+            repositories.add(repoInfo.getRepository());
+        }
+        return repositories;
+    }
+
     public List<FilesystemItem> getArchiveContent(BorgRepoConfig repoConfig, Archive archive) {
         if (archive == null || StringUtils.isBlank(archive.getArchive())) {
             return null;
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoInfoCache.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoInfoCache.java
index b71fb55..703e504 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoInfoCache.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoInfoCache.java
@@ -20,17 +20,17 @@
     }
 
     @Override
-    public boolean matches(RepoInfo element, String identifier) {
+    protected boolean matches(RepoInfo element, String identifier) {
         return element.matches(identifier);
     }
 
     @Override
-    public String getIdentifier(RepoInfo element) {
+    protected String getIdentifier(RepoInfo element) {
         return element.getRepository().getId();
     }
 
     @Override
-    public void updateFrom(RepoInfo dest, RepoInfo source) {
+    protected void updateFrom(RepoInfo dest, RepoInfo source) {
         dest.updateFrom(source);
     }
 
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoListCache.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoListCache.java
index ef5773d..d09ea39 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoListCache.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoListCache.java
@@ -20,17 +20,17 @@
     }
 
     @Override
-    public boolean matches(RepoList element, String identifier) {
+    protected boolean matches(RepoList element, String identifier) {
         return element.matches(identifier);
     }
 
     @Override
-    public String getIdentifier(RepoList element) {
+    protected String getIdentifier(RepoList element) {
         return element.getRepository().getId();
     }
 
     @Override
-    public void updateFrom(RepoList dest, RepoList source) {
+    protected void updateFrom(RepoList dest, RepoList source) {
         dest.updateFrom(source);
     }
 
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/config/ConfigurationHandler.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/config/ConfigurationHandler.java
index e3bf842..c85144d 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/config/ConfigurationHandler.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/config/ConfigurationHandler.java
@@ -29,7 +29,7 @@
         return instance.configuration;
     }
 
-    public void read() {
+    private void read() {
         log.info("Reading config file '" + configFile.getAbsolutePath() + "'");
         try {
             String json = FileUtils.readFileToString(configFile, Definitions.STD_CHARSET);
@@ -68,5 +68,6 @@
         configFile = new File(userHome, CONFIG_FILENAME);
         backupConfigFile = new File(userHome, CONFIG_BACKUP_FILENAME);
         workingDir = new File(userHome, APP_WORKING_DIR);
+        read();
     }
 }
diff --git a/borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java b/borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java
index 132765a..b8c70a2 100644
--- a/borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java
+++ b/borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java
@@ -23,7 +23,6 @@
     @Test
     void repoCacheTest() {
         ConfigurationHandler configHandler = ConfigurationHandler.getInstance();
-        configHandler.read();
         Configuration config = ConfigurationHandler.getConfiguration();
         if (config.getRepoConfigs().size() == 0) {
             log.info("No repos configured. Please configure repos first in: " + configHandler.getConfigFile().getAbsolutePath());
diff --git a/borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java b/borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java
index 4d8489f..b5be676 100644
--- a/borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java
+++ b/borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java
@@ -12,7 +12,6 @@
         File origConfigFile = new File(System.getProperty("user.home"), ".borgbutler-orig.json");
         FileUtils.copyFile(ConfigurationHandler.getInstance().getConfigFile(), origConfigFile);
         Configuration configuration = ConfigurationHandler.getConfiguration();
-        ConfigurationHandler.getInstance().read();
         ConfigurationHandler.getInstance().write();
         FileUtils.copyFile(origConfigFile, ConfigurationHandler.getInstance().getConfigFile());
     }
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java
new file mode 100644
index 0000000..23bcb52
--- /dev/null
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java
@@ -0,0 +1,58 @@
+package de.micromata.borgbutler.server.rest;
+
+import de.micromata.borgbutler.cache.ButlerCache;
+import de.micromata.borgbutler.json.JsonUtils;
+import de.micromata.borgbutler.json.borg.Repository;
+import org.apache.commons.collections4.CollectionUtils;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+@Path("/repos")
+public class ReposRest {
+    @GET
+    @Path("refresh")
+    @Produces(MediaType.TEXT_PLAIN)
+    /**
+     * Reloads all templates on the server.
+     * @return "OK"
+     */
+    public String refresh() {
+        ButlerCache.getInstance().getRepoInfoCache().clearAndReset();
+        return "OK";
+    }
+
+    @GET
+    @Path("repo")
+    @Produces(MediaType.APPLICATION_JSON)
+    /**
+     *
+     * @param id
+     * @param prettyPrinter If true then the json output will be in pretty format.
+     * @see JsonUtils#toJson(Object, boolean)
+     */
+    public String getTemplate(@QueryParam("id") String id, @QueryParam("prettyPrinter") boolean prettyPrinter) {
+        Repository repository = ButlerCache.getInstance().getRepository(id);
+        return JsonUtils.toJson(repository, prettyPrinter);
+    }
+
+    @GET
+    @Path("list")
+    @Produces(MediaType.APPLICATION_JSON)
+    /**
+     *
+     * @param prettyPrinter If true then the json output will be in pretty format.
+     * @see JsonUtils#toJson(Object, boolean)
+     */
+    public String getList(@QueryParam("prettyPrinter") boolean prettyPrinter) {
+        List<Repository> repositories = ButlerCache.getInstance().getAllRepositories();
+        if (CollectionUtils.isEmpty(repositories)) {
+            return "";
+        }
+        return JsonUtils.toJson(repositories, prettyPrinter);
+    }
+}
diff --git a/borgbutler-webapp/src/components/views/develop/RestServices.jsx b/borgbutler-webapp/src/components/views/develop/RestServices.jsx
index 2bb7977..76e4fde 100644
--- a/borgbutler-webapp/src/components/views/develop/RestServices.jsx
+++ b/borgbutler-webapp/src/components/views/develop/RestServices.jsx
@@ -93,6 +93,12 @@
                     Rest Services
                 </PageHeader>
                 <h3>
+                    Repositories
+                </h3>
+                <ul>
+                    <li><RestUrlLink service='repos/list'/></li>
+                </ul>
+                <h3>
                     Config
                 </h3>
                 <ul>
diff --git a/borgbutler-webapp/src/components/views/repos/RepoCard.jsx b/borgbutler-webapp/src/components/views/repos/RepoCard.jsx
new file mode 100644
index 0000000..e82a8e0
--- /dev/null
+++ b/borgbutler-webapp/src/components/views/repos/RepoCard.jsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import {Link} from 'react-router-dom';
+import {Card, CardBody, CardFooter, CardHeader} from 'reactstrap';
+import {formatDateTime} from "../../../utilities/global";
+
+class RepoCard extends React.Component {
+
+    buildItem = (label, content) => {
+        return <li className="list-group-item">{label}{content.map((line, index) => {
+            return <div className="card-list-entry" key={index}>{line[0]} <span
+                className={`card-list-entry-value ${line[2]}`}>{line[1]}</span>
+            </div>;
+        })}</li>;
+    }
+
+    render = () => {
+        const repo = this.props.repo;
+        let repoId = repo.id ? repo.id : repo.name;
+        let content = [[Name, repo.name, 'name']];
+        let repoText = this.buildItem(null, content);
+
+        return <React.Fragment>
+            <Card tag={Link} to={`/repos/${repo.primaryKey}`} outline color="success" className={'repo'}
+                  style={{backgroundColor: '#fff'}}>
+                <CardHeader>{repoId}</CardHeader>
+                <CardBody>
+                    <ul className="list-group list-group-flush">
+                        {repoText}
+                    </ul>
+                </CardBody>
+                <CardFooter><span className={'lastModified'}>{formatDateTime(repo.lastModified)}</span></CardFooter>
+            </Card>
+        </React.Fragment>
+    };
+
+    constructor(props) {
+        super(props);
+
+        this.buildItem = this.buildItem.bind(this);
+    }
+}
+
+export default repoCard;
diff --git a/borgbutler-webapp/src/components/views/repos/RepoListView.css b/borgbutler-webapp/src/components/views/repos/RepoListView.css
new file mode 100644
index 0000000..321ba8f
--- /dev/null
+++ b/borgbutler-webapp/src/components/views/repos/RepoListView.css
@@ -0,0 +1,44 @@
+a.card-link {
+    color: #111;
+}
+
+a.card-link:hover {
+    text-decoration: none;
+}
+
+a.card.template {
+    margin: 5pt;
+    cursor: pointer;
+}
+
+a.card.template div.card-footer {
+    color: #666;
+    font-size: 12px;
+}
+
+a.card.template:hover div.card-body,
+a.card.template:hover div.card-body li.list-group-item {
+    background-color: #f5f5f5;
+}
+
+a.card.template div.card-list-entry {
+    color: grey;
+    font-size: 80%;
+}
+
+a.card.template div.card-list-entry .card-list-entry-value {
+    font-style: italic;
+}
+
+a.card.template div.card-list-entry .filename {
+    font-family: monospace;
+}
+
+a.card.template div.card-footer .lastModified {
+    float:right;
+}
+
+a.card.template:hover div.card-header,
+a.card.template:hover div.card-footer {
+    background-color: #eeee;
+}
diff --git a/borgbutler-webapp/src/components/views/repos/RepoListView.jsx b/borgbutler-webapp/src/components/views/repos/RepoListView.jsx
new file mode 100644
index 0000000..301f1c8
--- /dev/null
+++ b/borgbutler-webapp/src/components/views/repos/RepoListView.jsx
@@ -0,0 +1,125 @@
+import React from 'react'
+import './RepoListView.css';
+import {CardDeck} from 'reactstrap';
+import {PageHeader} from '../../general/BootstrapComponents';
+import {getRestServiceUrl} from '../../../utilities/global';
+import ErrorAlert from '../../general/ErrorAlert';
+import TemplateCard from './TemplateCard';
+import {IconRefresh} from "../../general/IconComponents";
+import I18n from "../../general/translation/I18n";
+
+class RepoListView extends React.Component {
+
+
+    path = getRestServiceUrl('repos');
+    state = {
+        isFetching: false
+    };
+
+    componentDidMount = () => {
+        this.fetchTemplates();
+    };
+
+    fetchTemplates = () => {
+        this.setState({
+            isFetching: true,
+            failed: false,
+            definitions: undefined,
+            templates: undefined
+        });
+        fetch(`${this.path}/list`, {
+            method: 'GET',
+            headers: {
+                'Accept': 'application/json'
+            }
+        })
+            .then(response => response.json())
+            .then(json => {
+                const definitions = json.templateDefinitions.reduce((accumulator, current) => ({
+                    ...accumulator,
+                    [current.refId]: current
+                }), {});
+
+                const templates = json.templates.map(template => {
+                    if (typeof template.templateDefinition === 'object') {
+                        //console.log('refId: ' + template.templateDefinition.refId + ', templateDefinition: ' + JSON.stringify(template.templateDefinition))
+                        definitions[template.templateDefinition.refId] = template.templateDefinition;
+                        template.templateDefinition = template.templateDefinition.refId;
+                    }
+
+                    return {
+                        id: template.id,
+                        primaryKey: template.fileDescriptor.primaryKey,
+                        filename: template.fileDescriptor.filename,
+                        lastModified: template.fileDescriptor.lastModified,
+                        templateDefinitionId: template.templateDefinitionId,
+                        templateDefinition: template.templateDefinition
+                    };
+                });
+
+                this.setState({
+                    isFetching: false,
+                    definitions, templates
+                })
+            })
+            .catch(() => this.setState({isFetching: false, failed: true}));
+    };
+
+    render = () => {
+        let content = undefined;
+
+        if (this.state.isFetching) {
+
+            content = <i>Loading...</i>;
+
+        } else if (this.state.failed) {
+
+            content = <ErrorAlert
+                title={'Cannot load Templates'}
+                description={'Something went wrong during contacting the rest api.'}
+                action={{
+                    handleClick: this.fetchTemplates,
+                    title: 'Try again'
+                }}
+            />;
+
+        } else if (this.state.templates) {
+
+            content = <React.Fragment>
+                <div
+                    className={'btn btn-outline-primary refresh-button-right'}
+                    onClick={this.fetchTemplates}
+                >
+                    <IconRefresh/>
+                </div>
+                <CardDeck>
+                {this.state.templates.map(template => {
+                    const definition = this.state.definitions[template.templateDefinition];
+
+                    return <TemplateCard
+                        key={template.primaryKey}
+                        template={template}
+                        definition={definition}
+                    />;
+                })}
+                </CardDeck>
+            </React.Fragment>;
+
+        }
+
+        return <React.Fragment>
+            <PageHeader>
+                <I18n name={'templates'}/>
+            </PageHeader>
+            {content}
+        </React.Fragment>;
+    };
+
+    constructor(props) {
+        super(props);
+
+        this.fetchTemplates = this.fetchTemplates.bind(this);
+    }
+}
+
+export default RepoListView;

--
Gitblit v1.10.0