From 1dab687e769cc830adeb2ef53642abad77e1f7a6 Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Fri, 18 Jan 2019 15:35:50 +0000
Subject: [PATCH] BorgRepConfig...Repo config...

---
 borgbutler-core/src/main/java/de/micromata/borgbutler/config/BorgRepoConfig.java   |    9 +
 borgbutler-webapp/src/components/views/repos/RepoConfigPanel.jsx                   |   53 +++++---
 borgbutler-webapp/src/components/views/repos/RepoCard.jsx                          |    2 
 borgbutler-webapp/src/containers/WebApp.jsx                                        |    2 
 borgbutler-webapp/src/components/views/repos/RepoArchiveListView.jsx               |  204 +++++++++++++++++++--------------
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java       |   10 +
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java |   22 +++
 7 files changed, 187 insertions(+), 115 deletions(-)

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 2c45d9c..a25e288 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
@@ -124,9 +124,15 @@
         this.repoCacheAccess.clear();
     }
 
+    public void clearRepoCacheAccess(String repo) {
+        if (this.repoCacheAccess.get(repo) != null) {
+            log.info("Clearing repository cache '" + repo + "'...");
+            this.repoCacheAccess.remove(repo);
+        }
+    }
+
     public void clearRepoCacheAccess(Repository repository) {
-        log.info("Clearing repository cache '" + repository.getName() + "'...");
-        this.repoCacheAccess.remove(repository.getName());
+        clearRepoCacheAccess(repository.getName());
     }
 
     /**
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/config/BorgRepoConfig.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/config/BorgRepoConfig.java
index 0ffeecd..52ac43a 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/config/BorgRepoConfig.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/config/BorgRepoConfig.java
@@ -1,6 +1,5 @@
 package de.micromata.borgbutler.config;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.Getter;
 import lombok.Setter;
 import org.apache.commons.lang3.StringUtils;
@@ -29,7 +28,6 @@
     private String passwordCommand;
     @Getter
     @Setter
-    @JsonIgnore
     private String id;
 
     public String[] getEnvironmentVariables() {
@@ -51,4 +49,11 @@
         if (StringUtils.isBlank(value)) return;
         list.add(variable + "=" + value);
     }
+
+   public void copyFrom(BorgRepoConfig other) {
+        this.displayName = other.displayName;
+        this.repo = other.repo;
+        this.passphrase = other.passphrase;
+        this.passwordCommand = other.passwordCommand;
+   }
 }
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
index 125f2a0..8c4660e 100644
--- 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
@@ -10,10 +10,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
+import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import java.util.List;
 
@@ -68,6 +65,23 @@
         return JsonUtils.toJson(repoConfig, prettyPrinter);
     }
 
+    @POST
+    @Path("repoConfig")
+    @Produces(MediaType.TEXT_PLAIN)
+    public void setRepoConfig(String jsonConfig) {
+        BorgRepoConfig newRepoConfig = JsonUtils.fromJson(BorgRepoConfig.class, jsonConfig);
+        BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(newRepoConfig.getId());
+        if (repoConfig == null) {
+            log.error("Can't find repo config '" + newRepoConfig.getId() + "'. Can't save new settings.");
+            return;
+        }
+        ButlerCache.getInstance().clearRepoCacheAccess(repoConfig.getRepo());
+        ButlerCache.getInstance().clearRepoCacheAccess(newRepoConfig.getRepo());
+        repoConfig.copyFrom(newRepoConfig);
+        ConfigurationHandler.getInstance().save();
+    }
+
+
     /**
      *
      * @param id id or name of repo.
diff --git a/borgbutler-webapp/src/components/views/repos/RepoArchiveListView.jsx b/borgbutler-webapp/src/components/views/repos/RepoArchiveListView.jsx
index e92d80b..15e8331 100644
--- a/borgbutler-webapp/src/components/views/repos/RepoArchiveListView.jsx
+++ b/borgbutler-webapp/src/components/views/repos/RepoArchiveListView.jsx
@@ -1,5 +1,5 @@
 import React from 'react'
-import {Nav, NavLink, TabContent, Table, TabPane} from 'reactstrap';
+import {Badge, Nav, NavLink, TabContent, Table, TabPane} from 'reactstrap';
 import {Link} from "react-router-dom";
 import classNames from 'classnames';
 import {PageHeader} from '../../general/BootstrapComponents';
@@ -13,8 +13,10 @@
 
     state = {
         id: this.props.match.params.id,
+        displayName: this.props.match.params.displayName,
         isFetching: false,
         activeTab: '1',
+        redirectOnError: true
     };
 
     componentDidMount = () => {
@@ -43,7 +45,12 @@
                     repo: json
                 })
             })
-            .catch(() => this.setState({isFetching: false, failed: true}));
+            .catch(() => {
+                this.setState({isFetching: false, failed: true})
+                if (this.state.redirectOnError && this.state.activeTab !== '3') {
+                    this.setState({activeTab: '3', redirectOnError: false});
+                }
+            });
     };
 
     toggleTab = tab => () => {
@@ -52,22 +59,46 @@
         })
     };
 
+    afterSave() {
+        if (!this.state.failed) {
+            this.setState({
+                activeTab: '1'
+            })
+        }
+    }
+
+    afterCancel() {
+        if (!this.state.failed) {
+            this.setState({
+                activeTab: '1'
+            })
+        }
+    }
+
     render = () => {
-        let content = undefined;
+        let errorBadge = '';
+        let content1 = undefined;
+        let content2 = undefined;
         const repo = this.state.repo;
-        let pageHeader = '';
+        const displayName = (this.state.displayName) ? this.state.displayName : `Error: id=${this.state.id}`;
+        let pageHeader = <React.Fragment>
+            {displayName}
+        </React.Fragment>;
 
         if (this.state.isFetching) {
-            content = <JobMonitorPanel repo={this.state.id}/>;
+            content1 = <JobMonitorPanel repo={this.state.id}/>;
+            content2 = content1;
         } else if (this.state.failed) {
-            content = <ErrorAlert
+            content1 = <ErrorAlert
                 title={'Cannot load Repositories'}
-                description={'Something went wrong during contacting the rest api.'}
+                description={'Something went wrong, may-be wrong configuration?'}
                 action={{
                     handleClick: this.fetchRepo,
                     title: 'Try again'
                 }}
             />;
+            content2 = content1;
+            errorBadge = <Badge color="danger" pill>!</Badge>;
         } else if (this.state.repo) {
             pageHeader = <React.Fragment>
                 {repo.displayName}
@@ -128,91 +159,90 @@
                     <td>{repo.cache.path}</td>
                 </tr>
             }
-            content = <React.Fragment>
-                <Nav tabs>
-                    <NavLink
-                        className={classNames({active: this.state.activeTab === '1'})}
-                        onClick={this.toggleTab('1')}
-                    >
-                        Archives
-                    </NavLink>
-                    <NavLink
-                        className={classNames({active: this.state.activeTab === '2'})}
-                        onClick={this.toggleTab('2')}
-                    >
-                        Information
-                    </NavLink>
-                    <NavLink
-                        className={classNames({active: this.state.activeTab === '3'})}
-                        onClick={this.toggleTab('3')}
-                    >
-                        Configuration
-                    </NavLink>
-                </Nav>
-                <TabContent activeTab={this.state.activeTab}>
-                    <TabPane tabId={'1'}>
-                        <Table hover>
-                            <tbody>
-                            <tr>
-                                <th>Archive</th>
-                                <th>Time</th>
-                                <th></th>
-                                <th>Id</th>
-                            </tr>
-                            {repo.archives.map((archive) => {
-                                // Return the element. Also pass key
-                                let loaded = '';
-                                if (archive.fileListAlreadyCached) {
-                                    loaded = <IconCheck/>;
-                                }
-                                return (
-                                    <tr key={archive.id}>
-                                        <td><Link to={`/archives/${repo.id}/${archive.id}`}>{archive.name}</Link></td>
-                                        <td>{archive.time}</td>
-                                        <td>{loaded}</td>
-                                        <td>{archive.id}</td>
-                                    </tr>);
-                            })}
-                            </tbody>
-                        </Table>
-                    </TabPane>
-                    <TabPane tabId={'2'}>
-                        <Table striped bordered hover>
-                            <tbody>
-                            <tr>
-                                <td>Id</td>
-                                <td>{repo.id}</td>
-                            </tr>
-                            <tr>
-                                <td>Name</td>
-                                <td>{repo.name}</td>
-                            </tr>
-                            <tr>
-                                <td>Location</td>
-                                <td>{repo.location}</td>
-                            </tr>
-                            {stats}
-                            <tr>
-                                <td>Security dir</td>
-                                <td>{repo.securityDir}</td>
-                            </tr>
-                            {encryption}
-                            {cachePath}
-                            </tbody>
-                        </Table>
-                    </TabPane>
-                    <TabPane tabId={'3'}>
-                        <RepoConfigPanel id={repo.id}/>
-                    </TabPane>
-                </TabContent>
-            </React.Fragment>;
+            content1 = <Table hover>
+                <tbody>
+                <tr>
+                    <th>Archive</th>
+                    <th>Time</th>
+                    <th></th>
+                    <th>Id</th>
+                </tr>
+                {repo.archives.map((archive) => {
+                    // Return the element. Also pass key
+                    let loaded = '';
+                    if (archive.fileListAlreadyCached) {
+                        loaded = <IconCheck/>;
+                    }
+                    return (
+                        <tr key={archive.id}>
+                            <td><Link to={`/archives/${repo.id}/${archive.id}`}>{archive.name}</Link></td>
+                            <td>{archive.time}</td>
+                            <td>{loaded}</td>
+                            <td>{archive.id}</td>
+                        </tr>);
+                })}
+                </tbody>
+            </Table>;
+            content2 = <Table striped bordered hover>
+                <tbody>
+                <tr>
+                    <td>Id</td>
+                    <td>{repo.id}</td>
+                </tr>
+                <tr>
+                    <td>Name</td>
+                    <td>{repo.name}</td>
+                </tr>
+                <tr>
+                    <td>Location</td>
+                    <td>{repo.location}</td>
+                </tr>
+                {stats}
+                <tr>
+                    <td>Security dir</td>
+                    <td>{repo.securityDir}</td>
+                </tr>
+                {encryption}
+                {cachePath}
+                </tbody>
+            </Table>;
 
         }
         return <React.Fragment>
             <PageHeader>
                 {pageHeader}
             </PageHeader>
-            {content}
+            <Nav tabs>
+                <NavLink
+                    className={classNames({active: this.state.activeTab === '1'})}
+                    onClick={this.toggleTab('1')}
+                >
+                    Archives {errorBadge}
+                </NavLink>
+                <NavLink
+                    className={classNames({active: this.state.activeTab === '2'})}
+                    onClick={this.toggleTab('2')}
+                >
+                    Information {errorBadge}
+                </NavLink>
+                <NavLink
+                    className={classNames({active: this.state.activeTab === '3'})}
+                    onClick={this.toggleTab('3')}
+                >
+                    Configuration
+                </NavLink>
+            </Nav>
+            <TabContent activeTab={this.state.activeTab}>
+                <TabPane tabId={'1'}>
+                    {content1}
+                </TabPane>
+                <TabPane tabId={'2'}>
+                    {content2}
+                </TabPane>
+                <TabPane tabId={'3'}>
+                    <RepoConfigPanel id={this.state.id} afterCancel={this.afterCancel} afterSave={this.afterSave} repoError={this.state.failed}/>
+                </TabPane>
+            </TabContent>
         </React.Fragment>;
     };
 
@@ -221,6 +251,8 @@
 
         this.fetchRepo = this.fetchRepo.bind(this);
         this.toggleTab = this.toggleTab.bind(this);
+        this.afterCancel = this.afterCancel.bind(this);
+        this.afterSave = this.afterSave.bind(this);
     }
 }
 
diff --git a/borgbutler-webapp/src/components/views/repos/RepoCard.jsx b/borgbutler-webapp/src/components/views/repos/RepoCard.jsx
index b04cfad..b7ae760 100644
--- a/borgbutler-webapp/src/components/views/repos/RepoCard.jsx
+++ b/borgbutler-webapp/src/components/views/repos/RepoCard.jsx
@@ -18,7 +18,7 @@
         let repoText = this.buildItem(null, content);
 
         return <React.Fragment>
-            <Card tag={Link} to={`/repoArchives/${repo.id}`} outline color="success" className={'repo'}
+            <Card tag={Link} to={`/repoArchives/${repo.id}/${repo.displayName}`} outline color="success" className={'repo'}
                   style={{backgroundColor: '#fff'}}>
                 <CardHeader>{repo.displayName}</CardHeader>
                 <CardBody>
diff --git a/borgbutler-webapp/src/components/views/repos/RepoConfigPanel.jsx b/borgbutler-webapp/src/components/views/repos/RepoConfigPanel.jsx
index ebb1c58..93a9303 100644
--- a/borgbutler-webapp/src/components/views/repos/RepoConfigPanel.jsx
+++ b/borgbutler-webapp/src/components/views/repos/RepoConfigPanel.jsx
@@ -7,9 +7,7 @@
 import PropTypes from "prop-types";
 import ErrorAlert from "../../general/ErrorAlert";
 
-class RepoConfigPanel
-    extends React
-        .Component {
+class RepoConfigPanel extends React.Component {
 
     constructor(props) {
         super(props);
@@ -61,27 +59,42 @@
         this.setState({repoConfig: {...this.state.repoConfig, [event.target.name]: event.target.value}});
     }
 
-    onSave(event) {
-        this.setState({
-            loading: true
-        })
-        this.setState({
-            loading: false
-        })
-        this.setReload();
+    async onSave(event) {
+        const response = fetch(getRestServiceUrl("repos/repoConfig"), {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/json'
+            },
+            body: JSON.stringify(this.state.repoConfig)
+        });
+        if (response) await response;
+        if (this.props.afterSave) {
+            this.props.afterSave();
+        }
     }
 
-    onCancel() {
-        this.setReload();
+    async onCancel() {
+        const response = this.fetch();
+        if (response) await response;
+        if (this.props.afterCancel) {
+            this.props.afterCancel();
+        }
     }
 
     render() {
         let content;
+        let repoError = '';
+        if (this.props.repoError) {
+            repoError =<ErrorAlert
+                title={'Cannot access repository'}
+                description={'Repo not available or mis-configured (please refer the log files for more details).'}
+            />
+        }
         if (this.state.isFetching) {
             content = <React.Fragment>Loading...</React.Fragment>;
         } else if (this.state.failed) {
             content = <ErrorAlert
-                title={'Cannot load config or repository'}
+                title={'Cannot load config of repository'}
                 description={'Something went wrong during contacting the rest api.'}
                 action={{
                     handleClick: this.fetchRepo,
@@ -109,7 +122,7 @@
                                          onChange={this.handleTextChange}
                                          placeholder="Enter the password command to get the command from."/>
                     <FormLabelInputField label={'Password'} fieldLength={6} type={'password'}
-                                         name={'passwordCommand'} value={repoConfig.password}
+                                         name={'passphrase'} value={repoConfig.passphrase}
                                          onChange={this.handleTextChange}
                                          hint={"It's recommended to use password command instead."}
                     />
@@ -125,13 +138,15 @@
                 <LoadingOverlay active={this.state.loading}/>
             </React.Fragment>;
         }
-        return <React.Fragment>{content}</React.Fragment>;
+        return <React.Fragment>{content}{repoError}</React.Fragment>;
     }
 }
 
-RepoConfigPanel
-    .propTypes = {
-    id: PropTypes.string
+RepoConfigPanel.propTypes = {
+    afterCancel: PropTypes.func.isRequired,
+    afterSave: PropTypes.func.isRequired,
+    id: PropTypes.string,
+    repoError: PropTypes.bool
 };
 
 export default RepoConfigPanel;
diff --git a/borgbutler-webapp/src/containers/WebApp.jsx b/borgbutler-webapp/src/containers/WebApp.jsx
index 90c5494..7f878c8 100644
--- a/borgbutler-webapp/src/containers/WebApp.jsx
+++ b/borgbutler-webapp/src/containers/WebApp.jsx
@@ -81,7 +81,7 @@
                                     />
                                 ))
                             }
-                            <Route path={'/repoArchives/:id'} component={RepoArchiveListView}/>
+                            <Route path={'/repoArchives/:id/:displayName'} component={RepoArchiveListView}/>
                             <Route path={'/archives/:repoId/:archiveId'} component={ArchiveView}/>
                         </Switch>
                     </div>

--
Gitblit v1.10.0