Skip to content

Commit

Permalink
add config validation
Browse files Browse the repository at this point in the history
  • Loading branch information
makamekm committed May 21, 2020
1 parent 7d4b061 commit 75156e1
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 27 deletions.
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@testing-library/jest-dom": "^5.5.0",
"@testing-library/react": "^10.0.2",
"@testing-library/user-event": "^10.0.2",
"@types/react-toastify": "^4.1.0",
"animejs": "^3.2.0",
"aws-sdk": "^2.672.0",
"classnames": "^2.2.6",
Expand Down Expand Up @@ -110,6 +111,7 @@
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1",
"react-spring": "^8.0.27",
"react-toastify": "^6.0.5",
"react-virtualized": "^9.21.2",
"recharts": "^2.0.0-beta.5",
"safe-eval": "^0.4.1",
Expand Down
97 changes: 90 additions & 7 deletions src/app/Configuration/Configuration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import classNames from "classnames";
import { toJS } from "mobx";
import { useLocalStore, observer } from "mobx-react";
import { toast } from "react-toastify";

import { HeaderMain } from "~/components/Blocks/HeaderMain";
import { ipc } from "~/shared/ipc";
Expand All @@ -13,6 +14,7 @@ import { ConfigurationRepositories } from "./ConfigurationRepositories";
import { ConfigurationForm } from "./ConfigurationForm";
import { ConfigurationAllUsers } from "./ConfigurationAllUsers";
import { useLayoutConfig } from "~/components/Layout/LayoutService";
import { Config } from "~/shared/Config";

export const Configuration = observer(() => {
const state = useLocalStore<ConfigurationState>(() => ({
Expand Down Expand Up @@ -93,9 +95,79 @@ export const Configuration = observer(() => {
});
return arr;
},
prepareConfig(config: Config) {
config.users.forEach((u) => {
u.refName = u.name;
});
config.repositories.forEach((u) => {
u.refName = u.name;
});
return config;
},
verify(config: Config) {
let errors: string[] = [];
const usedTeamDuplicates: string[] = [];
config.teams.forEach((t) => {
if (
config.teams.filter((tt) => tt.name === t.name).length > 1 &&
!usedTeamDuplicates.includes(t.name)
) {
errors.push("The team name has duplicates: " + t.name);
usedTeamDuplicates.push(t.name);
}
});
const usedUserDuplicates: string[] = [];
config.users.forEach((t) => {
if (
config.users.filter((tt) => tt.name === t.name).length > 1 &&
!usedUserDuplicates.includes(t.name)
) {
errors.push("The user name has duplicates: " + t.name);
usedUserDuplicates.push(t.name);
}
});
const usedRepositoryDuplicates: string[] = [];
config.repositories.forEach((t) => {
if (
config.repositories.filter((tt) => tt.name === t.name).length > 1 &&
!usedRepositoryDuplicates.includes(t.name)
) {
errors.push("The repository name has duplicates: " + t.name);
usedRepositoryDuplicates.push(t.name);
}
});
return errors;
},
prepareSaveConfig(config: Config) {
config.teams.forEach((t) => {
t.users = t.users.map((name) => {
const u = config.users.find((u) => u.refName === name);
if (u) {
return u.name;
} else {
return name;
}
});
t.repositories = t.repositories.map((name) => {
const u = config.repositories.find((u) => u.refName === name);
if (u) {
return u.name;
} else {
return name;
}
});
});
config.users.forEach((u) => {
delete u.refName;
});
config.repositories.forEach((u) => {
delete u.refName;
});
return config;
},
load: async () => {
state.isLoading = true;
state.config = await ipc.handlers.GET_CONFIG(true);
state.config = state.prepareConfig(await ipc.handlers.GET_CONFIG(true));
state.allUsers = await ipc.handlers.GET_REPOSITORY_USERS();
state.isDirty = false;
state.isLoading = false;
Expand All @@ -104,12 +176,23 @@ export const Configuration = observer(() => {
if (!state.isDirty) {
return;
}
state.isLoading = true;
await ipc.handlers.SAVE_CONFIG(toJS(state.config));
state.config = await ipc.handlers.GET_CONFIG(true);
state.allUsers = await ipc.handlers.GET_REPOSITORY_USERS();
state.isDirty = false;
state.isLoading = false;
const errors = state.verify(toJS(state.config));
if (errors.length === 0) {
state.isLoading = true;
await ipc.handlers.SAVE_CONFIG(
state.prepareSaveConfig(toJS(state.config))
);
state.config = state.prepareConfig(await ipc.handlers.GET_CONFIG(true));
state.allUsers = await ipc.handlers.GET_REPOSITORY_USERS();
state.isDirty = false;
state.isLoading = false;
} else {
errors.forEach((error) => {
toast(error, {
type: "error",
});
});
}
},
}));

Expand Down
1 change: 0 additions & 1 deletion src/app/Configuration/ConfigurationTeams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ export const ConfigurationTeams = observer(
<Typeahead
placeholder="Add repositories..."
multiple
allowNew
autoFocus
selected={team.repositories}
onChange={(selected) => {
Expand Down
52 changes: 33 additions & 19 deletions src/app/Layout/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import classNames from "classnames";
import { ToastContainer } from "react-toastify";
import { LayoutService } from "~/components/Layout/LayoutService";
import { observer } from "mobx-react";
import { useRouteMatch } from "react-router";
Expand Down Expand Up @@ -234,27 +235,40 @@ export const AppLayout: React.FC = observer(({ children }) => {
const service = React.useContext(LayoutService);
const scrollable = service.scrollable && service.nonScrollableStack === 0;
return (
<div className="min-h-screen">
<div className="lg:flex">
{service.sidebar && <SideMenu />}
<div className="relative flex-1 flex flex-col min-h-screen">
<div className="content container mx-auto flex-1">
{service.topbar && <TopMenu />}
{children}
{service.footer && (
<div className="text-gray-600 dark-mode:text-gray-300 text-center text-xs pb-2 pt-5 mx-auto no-print">
<FooterText />
</div>
)}
<>
<ToastContainer
position="bottom-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={true}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
<div className="min-h-screen">
<div className="lg:flex">
{service.sidebar && <SideMenu />}
<div className="relative flex-1 flex flex-col min-h-screen">
<div className="content container mx-auto flex-1">
{service.topbar && <TopMenu />}
{children}
{service.footer && (
<div className="text-gray-600 dark-mode:text-gray-300 text-center text-xs pb-2 pt-5 mx-auto no-print">
<FooterText />
</div>
)}
</div>
</div>
</div>
<style jsx>{`
:global(body) {
max-height: ${scrollable ? "unset" : "100vh"};
overflow-y: ${scrollable ? "visible" : "hidden"};
}
`}</style>
</div>
<style jsx>{`
:global(body) {
max-height: ${scrollable ? "unset" : "100vh"};
overflow-y: ${scrollable ? "visible" : "hidden"};
}
`}</style>
</div>
</>
);
});
2 changes: 2 additions & 0 deletions src/shared/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ export interface Config {
id: string;
url: string;
name: string;
refName?: string;
branch?: string;
exclude?: string[];
}[];
users: {
id: string;
refName?: string;
name: string;
associations: string[];
}[];
Expand Down
9 changes: 9 additions & 0 deletions src/styles/components/toastify.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import "~react-toastify/dist/ReactToastify.css";

.Toastify__toast-body {
padding: 0rem 0rem 0rem 0.5rem;
}

.Toastify__toast {
padding: 1rem 1rem;
}
1 change: 1 addition & 0 deletions src/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@import "./colors.scss";
@import "./base.scss";

@import "./components/toastify.scss";
@import "./components/bounce-animation.scss";
@import "./components/hr-text.scss";
@import "./components/print.scss";
Expand Down

0 comments on commit 75156e1

Please sign in to comment.