refactor UI

This commit is contained in:
Eduard Heimbuch
2020-11-30 16:20:39 +01:00
parent 5468fddf90
commit af9f6ab629
19 changed files with 503 additions and 403 deletions

View File

@@ -49,3 +49,9 @@ const pathRegex = /^((?!\/{2,}).)*$/;
export const isPathValid = (path: string) => { export const isPathValid = (path: string) => {
return pathRegex.test(path); return pathRegex.test(path);
}; };
const urlRegex = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
export const isUrlValid = (url: string) => {
return urlRegex.test(url);
};

View File

@@ -29,7 +29,7 @@ export { Me } from "./Me";
export { DisplayedUser, User } from "./User"; export { DisplayedUser, User } from "./User";
export { Group, Member } from "./Group"; export { Group, Member } from "./Group";
export { Repository, RepositoryCollection, RepositoryGroup, RepositoryCreation, Namespace, NamespaceCollection } from "./Repositories"; export { Repository, RepositoryCollection, RepositoryGroup, RepositoryCreation, Namespace, NamespaceCollection, RepositoryUrlImport } from "./Repositories";
export { RepositoryType, RepositoryTypeCollection } from "./RepositoryTypes"; export { RepositoryType, RepositoryTypeCollection } from "./RepositoryTypes";
export { Branch, BranchRequest } from "./Branches"; export { Branch, BranchRequest } from "./Branches";

View File

@@ -12,6 +12,7 @@
"namespace-invalid": "Der Namespace des Repository ist ungültig", "namespace-invalid": "Der Namespace des Repository ist ungültig",
"name-invalid": "Der Name des Repository ist ungültig", "name-invalid": "Der Name des Repository ist ungültig",
"contact-invalid": "Der Kontakt muss eine gültige E-Mail Adresse sein", "contact-invalid": "Der Kontakt muss eine gültige E-Mail Adresse sein",
"url-invalid": "Die URL ist ungültig",
"branch": { "branch": {
"nameInvalid": "Der Name des Branches ist ungültig" "nameInvalid": "Der Name des Branches ist ungültig"
} }
@@ -68,6 +69,12 @@
"pending": { "pending": {
"subtitle": "Repository wird importiert...", "subtitle": "Repository wird importiert...",
"infoText": "Ihr Repository wird gerade importiert. Dies kann einen Moment dauern. Sie werden weitergeleitet, sobald der Import abgeschlossen ist. Wenn Sie diese Seite verlassen, können Sie nicht zurückkehren, um den Import-Status zu erfahren." "infoText": "Ihr Repository wird gerade importiert. Dies kann einen Moment dauern. Sie werden weitergeleitet, sobald der Import abgeschlossen ist. Wenn Sie diese Seite verlassen, können Sie nicht zurückkehren, um den Import-Status zu erfahren."
},
"importTypes": {
"url": {
"label": "Import via URL",
"helpText": "Das Repository wird über eine URL importiert."
}
} }
}, },
"branches": { "branches": {

View File

@@ -12,6 +12,7 @@
"namespace-invalid": "The repository namespace is invalid", "namespace-invalid": "The repository namespace is invalid",
"name-invalid": "The repository name is invalid", "name-invalid": "The repository name is invalid",
"contact-invalid": "Contact must be a valid mail address", "contact-invalid": "Contact must be a valid mail address",
"url-invalid": "The URL is invalid",
"branch": { "branch": {
"nameInvalid": "The branch name is invalid" "nameInvalid": "The branch name is invalid"
} }
@@ -69,6 +70,12 @@
"pending": { "pending": {
"subtitle": "Importing Repository...", "subtitle": "Importing Repository...",
"infoText": "Your repository is currently being imported. This may take a moment. You will be forwarded as soon as the import is finished. If you leave this page you cannot return to find out the import status." "infoText": "Your repository is currently being imported. This may take a moment. You will be forwarded as soon as the import is finished. If you leave this page you cannot return to find out the import status."
},
"importTypes": {
"url": {
"label": "Import via URL",
"helpText": "The Repository will be imported via the provided URL."
}
} }
}, },
"branches": { "branches": {

View File

@@ -0,0 +1,98 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { FC, useState } from "react";
import { RepositoryUrlImport } from "@scm-manager/ui-types";
import { InputField, validation } from "@scm-manager/ui-components";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
type Props = {
repository: RepositoryUrlImport;
onChange: (repository: RepositoryUrlImport) => void;
setValid: (valid: boolean) => void;
};
const Column = styled.div`
padding: 0 0.75rem;
`;
const Columns = styled.div`
padding: 0.75rem 0 0;
`;
const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
const [t] = useTranslation("repos");
const [urlValidationError, setUrlValidationError] = useState(false);
const handleImportUrlChange = (importUrl: string) => {
const changedRepo = { ...repository, importUrl };
if (!repository.name) {
// If the repository name is not fill we set a name suggestion
const match = importUrl.match(/([^\/]+?)(?:.git)?$/);
if (match && match[1]) {
changedRepo.name = match[1];
}
}
onChange(changedRepo);
const valid = validation.isUrlValid(importUrl);
setUrlValidationError(!valid);
setValid(valid);
};
return (
<Columns className="columns is-multiline">
<Column className="column is-full">
<InputField
label={t("import.importUrl")}
onChange={handleImportUrlChange}
value={repository.importUrl}
helpText={t("help.importUrlHelpText")}
validationError={urlValidationError}
errorMessage={t("validation.url-invalid")}
/>
</Column>
<Column className="column is-half">
<InputField
label={t("import.username")}
onChange={username => onChange({ ...repository, username })}
value={repository.username}
helpText={t("help.usernameHelpText")}
/>
</Column>
<Column className="column is-half">
<InputField
label={t("import.password")}
onChange={password => onChange({ ...repository, password })}
value={repository.password}
type="password"
helpText={t("help.passwordHelpText")}
/>
</Column>
</Columns>
);
};
export default ImportFromUrlForm;

View File

@@ -0,0 +1,109 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { FC, useState } from "react";
import NamespaceAndNameFields from "./NamespaceAndNameFields";
import { Repository, RepositoryUrlImport } from "@scm-manager/ui-types";
import ImportFromUrlForm from "./ImportFromUrlForm";
import RepositoryInformationForm from "./RepositoryInformationForm";
import { apiClient, ErrorNotification, Level, SubmitButton } from "@scm-manager/ui-components";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
type Props = {
url: string;
setImportPending: (pending: boolean) => void;
};
const ImportRepositoryFromUrl: FC<Props> = ({ url, setImportPending }) => {
const [repo, setRepo] = useState<RepositoryUrlImport>({
name: "",
namespace: "",
type: "",
contact: "",
description: "",
importUrl: "",
username: "",
password: "",
_links: {}
});
const [valid, setValid] = useState({ namespaceAndName: false, contact: true, importUrl: false });
const isValid = () => {
return Object.values(valid).every(v => v);
};
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | undefined>();
const history = useHistory();
const [t] = useTranslation("repos");
const handleImportLoading = (loading: boolean) => {
setLoading(loading);
setImportPending(loading);
};
const submit = () => {
handleImportLoading(true);
apiClient
.post(url, repo, "application/vnd.scmm-repository+json;v=2")
.then(response => {
const location = response.headers.get("Location");
// @ts-ignore Location is always set if the repository import was successful
return apiClient.get(location);
})
.then(response => response.json())
.then(repo => history.push(`/repo/${repo.namespace}/${repo.name}/code/sources`))
.catch(error => {
setError(error);
handleImportLoading(false);
});
};
return (
<form onSubmit={submit}>
<ErrorNotification error={error} />
<ImportFromUrlForm
repository={repo}
onChange={setRepo}
setValid={(importUrl: boolean) => setValid({ ...valid, importUrl })}
/>
<hr />
<NamespaceAndNameFields
repository={repo}
onChange={setRepo as React.Dispatch<React.SetStateAction<Repository>>}
setValid={(namespaceAndName: boolean) => setValid({ ...valid, namespaceAndName })}
/>
<RepositoryInformationForm
repository={repo}
onChange={setRepo as React.Dispatch<React.SetStateAction<Repository>>}
disabled={false}
setValid={(contact: boolean) => setValid({ ...valid, contact })}
/>
<Level
right={<SubmitButton disabled={!isValid()} loading={loading} label={t("repositoryForm.submitImport")} />}
/>
</form>
);
};
export default ImportRepositoryFromUrl;

View File

@@ -24,6 +24,7 @@
import React, { FC } from "react"; import React, { FC } from "react";
import { RepositoryType, Link } from "@scm-manager/ui-types"; import { RepositoryType, Link } from "@scm-manager/ui-types";
import { Radio } from "@scm-manager/ui-components"; import { Radio } from "@scm-manager/ui-components";
import { useTranslation } from "react-i18next";
type Props = { type Props = {
repositoryType: RepositoryType; repositoryType: RepositoryType;
@@ -31,7 +32,8 @@ type Props = {
setImportType: (type: string) => void; setImportType: (type: string) => void;
}; };
const ImportTypeSelect: FC<Props> = ({repositoryType, importType, setImportType}) => { const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType }) => {
const [t] = useTranslation("repos");
const changeImportType = (checked: boolean, name?: string) => { const changeImportType = (checked: boolean, name?: string) => {
if (name && checked) { if (name && checked) {
@@ -39,16 +41,17 @@ const ImportTypeSelect: FC<Props> = ({repositoryType, importType, setImportType}
} }
}; };
//TODO Add helptext translation
return ( return (
<> <>
{(repositoryType._links.import as Link[]).map(type => ( {(repositoryType._links.import as Link[]).map((type, index) => (
<Radio <Radio
name={type.name} name={type.name}
checked={importType === type.name} checked={importType === type.name}
value={type.name} value={type.name}
label={type.name} label={t(`import.importTypes.${type.name}.label`)}
helpText={t(`import.importTypes.${type.name}.helpText`)}
onChange={changeImportType} onChange={changeImportType}
key={index}
/> />
))} ))}
</> </>

View File

@@ -0,0 +1,117 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { FC, useEffect, useState } from "react";
import { Repository } from "@scm-manager/ui-types";
import { CUSTOM_NAMESPACE_STRATEGY } from "../modules/repos";
import { useTranslation } from "react-i18next";
import { InputField } from "@scm-manager/ui-components";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import * as validator from "./form/repositoryValidation";
import { getNamespaceStrategies } from "../../admin/modules/namespaceStrategies";
import { connect } from "react-redux";
type Props = {
repository: Repository;
onChange: (repository: Repository) => void;
namespaceStrategy: string;
setValid: (valid: boolean) => void;
};
const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStrategy, setValid }) => {
const [t] = useTranslation("repos");
const [namespaceValidationError, setNamespaceValidationError] = useState(false);
const [nameValidationError, setNameValidationError] = useState(false);
useEffect(() => {
//TODO fix validation
if (repository.name) {
const valid = validator.isNameValid(repository.name);
setNameValidationError(!valid);
onFieldChange();
}
}, [repository.name]);
const handleNamespaceChange = (namespace: string) => {
const valid = validator.isNamespaceValid(namespace);
setNamespaceValidationError(!valid);
onFieldChange(valid);
onChange({ ...repository, namespace });
};
const handleNameChange = (name: string) => {
const valid = validator.isNameValid(name);
setNameValidationError(!valid);
onFieldChange();
onChange({ ...repository, name });
};
//TODO fix validation
const onFieldChange = (namespaceValid?: boolean) => {
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY && !validator.isNamespaceValid(repository.namespace)) {
setValid(false);
} else {
setValid(!nameValidationError && !namespaceValidationError);
}
};
const renderNamespaceField = () => {
const props = {
label: t("repository.namespace"),
helpText: t("help.namespaceHelpText"),
value: repository ? repository.namespace : "",
onChange: handleNamespaceChange,
errorMessage: t("validation.namespace-invalid"),
validationError: namespaceValidationError
};
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY) {
return <InputField {...props} />;
}
return <ExtensionPoint name="repos.create.namespace" props={props} renderAll={false} />;
};
return (
<>
{renderNamespaceField()}
<InputField
label={t("repository.name")}
onChange={handleNameChange}
value={repository ? repository.name : ""}
validationError={nameValidationError}
errorMessage={t("validation.name-invalid")}
helpText={t("help.nameHelpText")}
/>
</>
);
};
const mapStateToProps = (state: any) => {
const namespaceStrategy = getNamespaceStrategies(state).current;
return {
namespaceStrategy
};
};
export default connect(mapStateToProps)(NamespaceAndNameFields);

View File

@@ -1,93 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { FC, useState } from "react";
import { InputField } from "@scm-manager/ui-components";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
type Props = {
url: string;
};
const Column = styled.div`
padding: 0 0.75rem;
`;
const Columns = styled.div`
padding: 0.75rem 0 0;
`;
const RepositoryImportFromUrl: FC<Props> = ({}) => {
const [name, setName] = useState("");
const [importUrl, setImportUrl] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [t] = useTranslation("repos");
const handleImportUrlChange = (url: string) => {
if (!name) {
// If the repository name is not fill we set a name suggestion
const match = url.match(/([^\/]+)(\.git)?/i);
if (match && match[1]) {
setName(match[1]);
}
}
setImportUrl(url);
};
return (
<>
<Columns className="columns is-multiline">
<Column className="column is-full">
<InputField
label={t("import.importUrl")}
onChange={handleImportUrlChange}
value={importUrl}
helpText={t("help.importUrlHelpText")}
/>
</Column>
<Column className="column is-half">
<InputField
label={t("import.username")}
onChange={setUsername}
value={username}
helpText={t("help.usernameHelpText")}
/>
</Column>
<Column className="column is-half">
<InputField
label={t("import.password")}
onChange={setPassword}
value={password}
type="password"
helpText={t("help.passwordHelpText")}
/>
</Column>
</Columns>
<hr />
</>
);
};
export default RepositoryImportFromUrl;

View File

@@ -0,0 +1,70 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { FC, useState } from "react";
import { InputField, Textarea } from "@scm-manager/ui-components";
import { Repository } from "@scm-manager/ui-types";
import { useTranslation } from "react-i18next";
import * as validator from "./form/repositoryValidation";
type Props = {
repository: Repository;
onChange: (repository: Repository) => void;
disabled: boolean;
setValid: (valid: boolean) => void;
};
const RepositoryInformationForm: FC<Props> = ({ repository, onChange, disabled, setValid }) => {
const [t] = useTranslation("repos");
const [contactValidationError, setContactValidationError] = useState(false);
const handleContactChange = (contact: string) => {
const valid = validator.isContactValid(contact);
setContactValidationError(!valid);
setValid(valid);
onChange({ ...repository, contact });
};
return (
<>
<InputField
label={t("repository.contact")}
onChange={handleContactChange}
value={repository ? repository.contact : ""}
validationError={contactValidationError}
errorMessage={t("validation.contact-invalid")}
helpText={t("help.contactHelpText")}
disabled={disabled}
/>
<Textarea
label={t("repository.description")}
onChange={description => onChange({ ...repository, description })}
value={repository ? repository.description : ""}
helpText={t("help.descriptionHelpText")}
disabled={disabled}
/>
</>
);
};
export default RepositoryInformationForm;

View File

@@ -25,10 +25,11 @@ import React, { FC, useEffect, useState } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { ExtensionPoint } from "@scm-manager/ui-extensions"; import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { Repository, RepositoryImport, RepositoryType } from "@scm-manager/ui-types"; import { Repository, RepositoryType } from "@scm-manager/ui-types";
import { Checkbox, InputField, Level, Select, SubmitButton, Textarea } from "@scm-manager/ui-components"; import { Checkbox, Level, Select, SubmitButton } from "@scm-manager/ui-components";
import * as validator from "./repositoryValidation";
import { CUSTOM_NAMESPACE_STRATEGY } from "../../modules/repos"; import { CUSTOM_NAMESPACE_STRATEGY } from "../../modules/repos";
import NamespaceAndNameFields from "../NamespaceAndNameFields";
import RepositoryInformationForm from "../RepositoryInformationForm";
const CheckboxWrapper = styled.div` const CheckboxWrapper = styled.div`
margin-top: 2em; margin-top: 2em;
@@ -44,17 +45,14 @@ const SpaceBetween = styled.div`
justify-content: space-between; justify-content: space-between;
`; `;
type Props = { type Props = {
createRepository?: (repo: RepositoryCreation, shouldInit: boolean) => void; createRepository?: (repo: RepositoryCreation, shouldInit: boolean) => void;
importRepository?: (repo: RepositoryImport) => void;
modifyRepository?: (repo: Repository) => void; modifyRepository?: (repo: Repository) => void;
repository?: Repository; repository?: Repository;
repositoryTypes?: RepositoryType[]; repositoryTypes?: RepositoryType[];
namespaceStrategy?: string; namespaceStrategy?: string;
loading?: boolean; loading?: boolean;
indexResources?: any; indexResources?: any;
creationMode?: "CREATE" | "IMPORT";
}; };
type RepositoryCreation = Repository & { type RepositoryCreation = Repository & {
@@ -64,13 +62,11 @@ type RepositoryCreation = Repository & {
const RepositoryForm: FC<Props> = ({ const RepositoryForm: FC<Props> = ({
createRepository, createRepository,
modifyRepository, modifyRepository,
importRepository,
repository, repository,
repositoryTypes, repositoryTypes,
namespaceStrategy, namespaceStrategy,
loading, loading,
indexResources, indexResources
creationMode
}) => { }) => {
const [repo, setRepo] = useState<Repository>({ const [repo, setRepo] = useState<Repository>({
name: "", name: "",
@@ -81,14 +77,9 @@ const RepositoryForm: FC<Props> = ({
_links: {} _links: {}
}); });
const [initRepository, setInitRepository] = useState(false); const [initRepository, setInitRepository] = useState(false);
const [namespaceValidationError, setNamespaceValidationError] = useState(false);
const [nameValidationError, setNameValidationError] = useState(false);
const [contactValidationError, setContactValidationError] = useState(false);
const [contextEntries, setContextEntries] = useState({}); const [contextEntries, setContextEntries] = useState({});
const [importUrl, setImportUrl] = useState(""); //TODO fix validation
const [username, setUsername] = useState(""); const [valid, setValid] = useState({ namespaceAndName: false, contact: true });
const [password, setPassword] = useState("");
const [t] = useTranslation("repos"); const [t] = useTranslation("repos");
useEffect(() => { useEffect(() => {
@@ -97,29 +88,21 @@ const RepositoryForm: FC<Props> = ({
} }
}, [repository]); }, [repository]);
const isImportMode = () => creationMode === "IMPORT";
const isCreateMode = () => creationMode === "CREATE";
const isEditMode = () => !!repository; const isEditMode = () => !!repository;
const isModifiable = () => !!repository && !!repository._links.update; const isModifiable = () => !!repository && !!repository._links.update;
const disabled = (!isModifiable() && isEditMode()) || loading; const disabled = (!isModifiable() && isEditMode()) || loading;
const isValid = () => { const isValid = () => {
return !( return (
namespaceValidationError || !(!repo.name || (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY && !repo.namespace)) &&
nameValidationError || Object.values(valid).every(v => v)
contactValidationError ||
!repo.name ||
(namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY && !repo.namespace) ||
(isImportMode() && !importUrl)
); );
}; };
const submit = (event: React.FormEvent<HTMLFormElement>) => { const submit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
if (isValid()) { if (isValid()) {
if (importRepository && isImportMode()) { if (createRepository) {
importRepository({ ...repo, url: importUrl, username, password });
} else if (createRepository && isCreateMode()) {
createRepository({ ...repo, contextEntries }, initRepository); createRepository({ ...repo, contextEntries }, initRepository);
} else if (modifyRepository) { } else if (modifyRepository) {
modifyRepository(repo); modifyRepository(repo);
@@ -139,23 +122,6 @@ const RepositoryForm: FC<Props> = ({
return []; return [];
}; };
const renderNamespaceField = () => {
const props = {
label: t("repository.namespace"),
helpText: t("help.namespaceHelpText"),
value: repo ? repo.namespace : "",
onChange: handleNamespaceChange,
errorMessage: t("validation.namespace-invalid"),
validationError: namespaceValidationError
};
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY) {
return <InputField {...props} />;
}
return <ExtensionPoint name="repos.create.namespace" props={props} renderAll={false} />;
};
const renderCreateOnlyFields = () => { const renderCreateOnlyFields = () => {
if (isEditMode()) { if (isEditMode()) {
return null; return null;
@@ -167,15 +133,10 @@ const RepositoryForm: FC<Props> = ({
}; };
return ( return (
<> <>
{renderNamespaceField()} <NamespaceAndNameFields
<InputField repository={repo}
label={t("repository.name")} onChange={setRepo}
onChange={handleNameChange} setValid={namespaceAndName => setValid({ ...valid, namespaceAndName })}
value={repo ? repo.name : ""}
validationError={nameValidationError}
errorMessage={t("validation.name-invalid")}
helpText={t("help.nameHelpText")}
disabled={disabled}
/> />
<SpaceBetween> <SpaceBetween>
<SelectWrapper> <SelectWrapper>
@@ -188,7 +149,6 @@ const RepositoryForm: FC<Props> = ({
disabled={disabled} disabled={disabled}
/> />
</SelectWrapper> </SelectWrapper>
{!isImportMode() && (
<CheckboxWrapper> <CheckboxWrapper>
<Checkbox <Checkbox
label={t("repositoryForm.initializeRepository")} label={t("repositoryForm.initializeRepository")}
@@ -201,7 +161,6 @@ const RepositoryForm: FC<Props> = ({
<ExtensionPoint name="repos.create.initialize" props={extensionProps} renderAll={true} /> <ExtensionPoint name="repos.create.initialize" props={extensionProps} renderAll={true} />
)} )}
</CheckboxWrapper> </CheckboxWrapper>
)}
</SpaceBetween> </SpaceBetween>
</> </>
); );
@@ -218,52 +177,28 @@ const RepositoryForm: FC<Props> = ({
}); });
}; };
const handleNamespaceChange = (namespace: string) => {
setNamespaceValidationError(!validator.isNamespaceValid(namespace));
setRepo({ ...repo, namespace });
};
const handleNameChange = (name: string) => {
setNameValidationError(!validator.isNameValid(name));
setRepo({ ...repo, name });
};
const handleContactChange = (contact: string) => {
setContactValidationError(!validator.isContactValid(contact));
setRepo({ ...repo, contact });
};
const submitButton = () => { const submitButton = () => {
if (disabled) { if (disabled) {
return null; return null;
} }
const translationKey = isImportMode() ? "repositoryForm.submitImport" : "repositoryForm.submitCreate"; return (
return <Level right={<SubmitButton disabled={!isValid()} loading={loading} label={t(translationKey)} />} />; <Level
right={<SubmitButton disabled={!isValid()} loading={loading} label={t("repositoryForm.submitCreate")} />}
/>
);
}; };
return ( return (
<>
<form onSubmit={submit}> <form onSubmit={submit}>
{renderCreateOnlyFields()} {renderCreateOnlyFields()}
<InputField <RepositoryInformationForm
label={t("repository.contact")} repository={repo}
onChange={handleContactChange} onChange={setRepo}
value={repo ? repo.contact : ""} disabled={!createRepository}
validationError={contactValidationError} setValid={contact => setValid({ ...valid, contact })}
errorMessage={t("validation.contact-invalid")}
helpText={t("help.contactHelpText")}
disabled={disabled}
/>
<Textarea
label={t("repository.description")}
onChange={description => setRepo({ ...repo, description })}
value={repo ? repo.description : ""}
helpText={t("help.descriptionHelpText")}
disabled={disabled}
/> />
{submitButton()} {submitButton()}
</form> </form>
</>
); );
}; };

View File

@@ -25,14 +25,8 @@ import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { WithTranslation, withTranslation } from "react-i18next"; import { WithTranslation, withTranslation } from "react-i18next";
import { History } from "history"; import { History } from "history";
import { import { NamespaceStrategies, Repository, RepositoryCreation, RepositoryType } from "@scm-manager/ui-types";
NamespaceStrategies, import { Page } from "@scm-manager/ui-components";
Repository,
RepositoryCreation,
RepositoryImport,
RepositoryType
} from "@scm-manager/ui-types";
import { Loading, Notification, Page } from "@scm-manager/ui-components";
import { import {
fetchRepositoryTypesIfNeeded, fetchRepositoryTypesIfNeeded,
getFetchRepositoryTypesFailure, getFetchRepositoryTypesFailure,
@@ -45,11 +39,7 @@ import {
createRepo, createRepo,
createRepoReset, createRepoReset,
getCreateRepoFailure, getCreateRepoFailure,
getImportRepoFailure, isCreateRepoPending
importRepoFromUrl,
importRepoReset,
isCreateRepoPending,
isImportRepoPending
} from "../modules/repos"; } from "../modules/repos";
import { getRepositoriesLink } from "../../modules/indexResource"; import { getRepositoriesLink } from "../../modules/indexResource";
import { import {
@@ -68,7 +58,6 @@ type Props = WithTranslation &
namespaceStrategies: NamespaceStrategies; namespaceStrategies: NamespaceStrategies;
pageLoading: boolean; pageLoading: boolean;
createLoading: boolean; createLoading: boolean;
importLoading: boolean;
error: Error; error: Error;
repoLink: string; repoLink: string;
indexResources: any; indexResources: any;
@@ -82,7 +71,6 @@ type Props = WithTranslation &
initRepository: boolean, initRepository: boolean,
callback: (repo: Repository) => void callback: (repo: Repository) => void
) => void; ) => void;
importRepoFromUrl: (link: string, repository: RepositoryImport, callback: (repo: Repository) => void) => void;
resetForm: () => void; resetForm: () => void;
// context props // context props
@@ -114,29 +102,13 @@ class AddRepository extends React.Component<Props> {
isImportPage = () => this.resolveLocation() === "import"; isImportPage = () => this.resolveLocation() === "import";
isCreatePage = () => this.resolveLocation() === "create"; isCreatePage = () => this.resolveLocation() === "create";
getSubtitle = () => {
const { importLoading, t } = this.props;
let subtitle;
if (this.isCreatePage()) {
subtitle = t("create.subtitle");
} else if (!importLoading) {
subtitle = t("import.subtitle");
} else {
subtitle = t("import.pending.subtitle");
}
return subtitle;
};
render() { render() {
const { const {
pageLoading, pageLoading,
createLoading, createLoading,
importLoading,
repositoryTypes, repositoryTypes,
namespaceStrategies, namespaceStrategies,
createRepo, createRepo,
importRepoFromUrl,
error, error,
indexResources, indexResources,
repoLink, repoLink,
@@ -146,37 +118,24 @@ class AddRepository extends React.Component<Props> {
return ( return (
<Page <Page
title={t("create.title")} title={t("create.title")}
subtitle={this.getSubtitle()} subtitle={t("create.subtitle")}
loading={pageLoading} loading={pageLoading}
error={error} error={error}
showContentOnError={true} showContentOnError={true}
> >
<> {!error && <RepositoryFormSwitcher creationMode={this.isImportPage() ? "IMPORT" : "CREATE"} />}
{/*//TODO fix this CSS*/} {this.isImportPage() && <ImportRepository repositoryTypes={repositoryTypes} />}
{!error && !importLoading && <RepositoryFormSwitcher creationMode={this.isImportPage() ? "IMPORT" : "CREATE"} />} {this.isCreatePage() && (
{importLoading && (
<>
<Notification type="info">{t("import.pending.infoText")}</Notification>
<Loading />
<hr/>
</>
)}
<ImportRepository repositoryTypes={repositoryTypes}/>
<RepositoryForm <RepositoryForm
repositoryTypes={repositoryTypes} repositoryTypes={repositoryTypes}
loading={createLoading || importLoading} loading={createLoading}
namespaceStrategy={namespaceStrategies.current} namespaceStrategy={namespaceStrategies.current}
createRepository={(repo, initRepository) => { createRepository={(repo, initRepository) => {
createRepo(repoLink, repo, initRepository, (repo: Repository) => this.repoCreated(repo)); createRepo(repoLink, repo, initRepository, (repo: Repository) => this.repoCreated(repo));
}} }}
importRepository={repo => {
//TODO Reset error if import started again
importRepoFromUrl(repoLink, repo, (repo: Repository) => this.repoCreated(repo));
}}
indexResources={indexResources} indexResources={indexResources}
creationMode={this.isImportPage() ? "IMPORT" : "CREATE"}
/> />
</> )}
</Page> </Page>
); );
} }
@@ -187,12 +146,8 @@ const mapStateToProps = (state: any) => {
const namespaceStrategies = getNamespaceStrategies(state); const namespaceStrategies = getNamespaceStrategies(state);
const pageLoading = isFetchRepositoryTypesPending(state) || isFetchNamespaceStrategiesPending(state); const pageLoading = isFetchRepositoryTypesPending(state) || isFetchNamespaceStrategiesPending(state);
const createLoading = isCreateRepoPending(state); const createLoading = isCreateRepoPending(state);
const importLoading = isImportRepoPending(state);
const error = const error =
getFetchRepositoryTypesFailure(state) || getFetchRepositoryTypesFailure(state) || getCreateRepoFailure(state) || getFetchNamespaceStrategiesFailure(state);
getCreateRepoFailure(state) ||
getFetchNamespaceStrategiesFailure(state) ||
getImportRepoFailure(state);
const repoLink = getRepositoriesLink(state); const repoLink = getRepositoriesLink(state);
const indexResources = state?.indexResources; const indexResources = state?.indexResources;
@@ -201,7 +156,6 @@ const mapStateToProps = (state: any) => {
namespaceStrategies, namespaceStrategies,
pageLoading, pageLoading,
createLoading, createLoading,
importLoading,
error, error,
repoLink, repoLink,
indexResources indexResources
@@ -219,12 +173,8 @@ const mapDispatchToProps = (dispatch: any) => {
createRepo: (link: string, repository: RepositoryCreation, initRepository: boolean, callback: () => void) => { createRepo: (link: string, repository: RepositoryCreation, initRepository: boolean, callback: () => void) => {
dispatch(createRepo(link, repository, initRepository, callback)); dispatch(createRepo(link, repository, initRepository, callback));
}, },
importRepoFromUrl: (link: string, repository: RepositoryImport, callback: () => void) => {
dispatch(importRepoFromUrl(link, repository, callback));
},
resetForm: () => { resetForm: () => {
dispatch(createRepoReset()); dispatch(createRepoReset());
dispatch(importRepoReset());
} }
}; };
}; };

View File

@@ -28,27 +28,30 @@ import { RepositoryType, Link } from "@scm-manager/ui-types";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ImportRepositoryTypeSelect from "../components/ImportRepositoryTypeSelect"; import ImportRepositoryTypeSelect from "../components/ImportRepositoryTypeSelect";
import ImportTypeSelect from "../components/ImportTypeSelect"; import ImportTypeSelect from "../components/ImportTypeSelect";
import RepositoryImportFromUrl from "../components/RepositoryImportFromUrl"; import ImportRepositoryFromUrl from "../components/ImportRepositoryFromUrl";
import { Loading, Notification } from "@scm-manager/ui-components";
type Props = { type Props = {
repositoryTypes: RepositoryType[]; repositoryTypes: RepositoryType[];
}; };
const ImportRepository: FC<Props> = ({ repositoryTypes }) => { const ImportRepository: FC<Props> = ({ repositoryTypes }) => {
const [importPending, setImportPending] = useState(false);
const [repositoryType, setRepositoryType] = useState<RepositoryType | undefined>(); const [repositoryType, setRepositoryType] = useState<RepositoryType | undefined>();
const [importType, setImportType] = useState(""); const [importType, setImportType] = useState("");
const [t] = useTranslation("repos"); const [t] = useTranslation("repos");
const changeRepositoryType = (repositoryType: RepositoryType) => { const changeRepositoryType = (repositoryType: RepositoryType) => {
setImportType(((repositoryType!._links.import as Link[])[0] as Link).name!);
setRepositoryType(repositoryType); setRepositoryType(repositoryType);
setImportType(repositoryType?._links ? ((repositoryType!._links?.import as Link[])[0] as Link).name! : "");
}; };
const renderImportComponent = () => { const renderImportComponent = () => {
if (importType === "url") { if (importType === "url") {
return ( return (
<RepositoryImportFromUrl <ImportRepositoryFromUrl
url={((repositoryType!._links.import as Link[])!.find((link: Link) => link.name === "url") as Link).href} url={((repositoryType!._links.import as Link[])!.find((link: Link) => link.name === "url") as Link).href}
setImportPending={setImportPending}
/> />
); );
} }
@@ -58,6 +61,13 @@ const ImportRepository: FC<Props> = ({ repositoryTypes }) => {
return ( return (
<div> <div>
{importPending && (
<>
<Notification type="info">{t("import.pending.infoText")}</Notification>
<Loading />
<hr />
</>
)}
<ImportRepositoryTypeSelect <ImportRepositoryTypeSelect
repositoryTypes={repositoryTypes} repositoryTypes={repositoryTypes}
repositoryType={repositoryType} repositoryType={repositoryType}
@@ -67,6 +77,7 @@ const ImportRepository: FC<Props> = ({ repositoryTypes }) => {
<> <>
<hr /> <hr />
<ImportTypeSelect repositoryType={repositoryType} importType={importType} setImportType={setImportType} /> <ImportTypeSelect repositoryType={repositoryType} importType={importType} setImportType={setImportType} />
<hr />
</> </>
)} )}
{importType && renderImportComponent()} {importType && renderImportComponent()}

View File

@@ -59,8 +59,6 @@ import reducer, {
getPermissionsLink, getPermissionsLink,
getRepository, getRepository,
getRepositoryCollection, getRepositoryCollection,
IMPORT_REPO_PENDING,
IMPORT_REPO_SUCCESS,
isAbleToCreateRepos, isAbleToCreateRepos,
isCreateRepoPending, isCreateRepoPending,
isDeleteRepoPending, isDeleteRepoPending,
@@ -71,10 +69,9 @@ import reducer, {
MODIFY_REPO_FAILURE, MODIFY_REPO_FAILURE,
MODIFY_REPO_PENDING, MODIFY_REPO_PENDING,
MODIFY_REPO_SUCCESS, MODIFY_REPO_SUCCESS,
modifyRepo, modifyRepo
importRepoFromUrl
} from "./repos"; } from "./repos";
import { Repository, RepositoryCollection, Link } from "@scm-manager/ui-types"; import { Link, Repository, RepositoryCollection } from "@scm-manager/ui-types";
const hitchhikerPuzzle42: Repository = { const hitchhikerPuzzle42: Repository = {
contact: "fourtytwo@hitchhiker.com", contact: "fourtytwo@hitchhiker.com",
@@ -414,70 +411,6 @@ describe("repos fetch", () => {
}); });
}); });
it("should successfully import repo hitchhiker/restatend", () => {
const importUrl = REPOS_URL + "/import/git/url";
const importRequest = {
...hitchhikerRestatend,
url: "https://scm-manager.org/scm/repo/secret/puzzle42",
username: "trillian",
password: "secret"
};
fetchMock.postOnce(importUrl, {
status: 201,
headers: {
location: "repositories/hitchhiker/restatend"
}
});
fetchMock.getOnce(REPOS_URL + "/hitchhiker/restatend", hitchhikerRestatend);
const expectedActions = [
{
type: IMPORT_REPO_PENDING
},
{
type: IMPORT_REPO_SUCCESS
}
];
const store = mockStore({});
return store.dispatch(importRepoFromUrl(URL, importRequest)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
it("should successfully import repo hitchhiker/restatend and call the callback", () => {
const importUrl = REPOS_URL + "/import/git/url";
const importRequest = {
...hitchhikerRestatend,
url: "https://scm-manager.org/scm/repo/secret/puzzle42",
username: "trillian",
password: "secret"
};
fetchMock.postOnce(importUrl, {
status: 201,
headers: {
location: "repositories/hitchhiker/restatend"
}
});
fetchMock.getOnce(REPOS_URL + "/hitchhiker/restatend", hitchhikerRestatend);
let callMe = "not yet";
const callback = (r: any) => {
expect(r).toEqual(hitchhikerRestatend);
callMe = "yeah";
};
const store = mockStore({});
return store.dispatch(importRepoFromUrl(URL, importRequest, callback)).then(() => {
expect(callMe).toBe("yeah");
});
});
it("should successfully create repo slarti/fjords", () => { it("should successfully create repo slarti/fjords", () => {
fetchMock.postOnce(REPOS_URL, { fetchMock.postOnce(REPOS_URL, {
status: 201, status: 201,

View File

@@ -26,13 +26,12 @@ import { apiClient } from "@scm-manager/ui-components";
import * as types from "../../modules/types"; import * as types from "../../modules/types";
import { import {
Action, Action,
Link,
Namespace, Namespace,
NamespaceCollection, NamespaceCollection,
Repository, Repository,
RepositoryCollection, RepositoryCollection,
RepositoryCreation, RepositoryCreation
RepositoryImport,
Link
} from "@scm-manager/ui-types"; } from "@scm-manager/ui-types";
import { isPending } from "../../modules/pending"; import { isPending } from "../../modules/pending";
import { getFailure } from "../../modules/failure"; import { getFailure } from "../../modules/failure";
@@ -241,58 +240,6 @@ export function fetchRepoFailure(namespace: string, name: string, error: Error):
}; };
} }
// import repo
export function importRepoFromUrl(link: string, repository: RepositoryImport, callback?: (repo: Repository) => void) {
const baseLink = link.endsWith("/") ? link : link + "/";
const importLink = baseLink + `import/${repository.type}/url`;
return function(dispatch: any) {
dispatch(importRepoPending());
return apiClient
.post(importLink, repository, CONTENT_TYPE)
.then(response => {
const location = response.headers.get("Location");
dispatch(importRepoSuccess());
// @ts-ignore Location is always set if the repository import was successful
return apiClient.get(location);
})
.then(response => response.json())
.then(response => {
if (callback) {
callback(response);
}
})
.catch(err => {
dispatch(importRepoFailure(err));
});
};
}
export function importRepoPending(): Action {
return {
type: IMPORT_REPO_PENDING
};
}
export function importRepoSuccess(): Action {
return {
type: IMPORT_REPO_SUCCESS
};
}
export function importRepoFailure(err: Error): Action {
return {
type: IMPORT_REPO_FAILURE,
payload: err
};
}
export function importRepoReset(): Action {
return {
type: IMPORT_REPO_RESET
};
}
// create repo // create repo
export function createRepo( export function createRepo(

View File

@@ -205,19 +205,19 @@ public class RepositoryImportResource {
checkNotNull(request, "request is required"); checkNotNull(request, "request is required");
checkArgument(!Strings.isNullOrEmpty(request.getName()), checkArgument(!Strings.isNullOrEmpty(request.getName()),
"request does not contain name of the repository"); "request does not contain name of the repository");
checkArgument(!Strings.isNullOrEmpty(request.getUrl()), checkArgument(!Strings.isNullOrEmpty(request.getImportUrl()),
"request does not contain url of the remote repository"); "request does not contain url of the remote repository");
Type t = type(type); Type t = type(type);
checkSupport(t, Command.PULL, request); checkSupport(t, Command.PULL, request);
logger.info("start {} import for external url {}", type, request.getUrl()); logger.info("start {} import for external url {}", type, request.getImportUrl());
Repository repository = null; Repository repository = new Repository(null, type, request.getNamespace(), request.getName());
try { try {
repository = manager.create( repository = manager.create(
new Repository(null, type, request.getNamespace(), request.getName()), repository,
pullChangesFromRemoteUrl(request) pullChangesFromRemoteUrl(request)
); );
} catch (Exception e) { } catch (Exception e) {
@@ -240,7 +240,7 @@ public class RepositoryImportResource {
.withPassword(request.getPassword()); .withPassword(request.getPassword());
} }
pullCommand.pull(request.getUrl()); pullCommand.pull(request.getImportUrl());
} catch (IOException e) { } catch (IOException e) {
throw new InternalRepositoryException(repository, "Failed to import from remote url", e); throw new InternalRepositoryException(repository, "Failed to import from remote url", e);
} }
@@ -537,7 +537,7 @@ public class RepositoryImportResource {
@NoArgsConstructor @NoArgsConstructor
@SuppressWarnings("java:S2160") @SuppressWarnings("java:S2160")
public static class RepositoryImportDto extends RepositoryDto { public static class RepositoryImportDto extends RepositoryDto {
private String url; private String importUrl;
private String username; private String username;
private String password; private String password;

View File

@@ -519,7 +519,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
Repository repository = RepositoryTestData.createHeartOfGold(); Repository repository = RepositoryTestData.createHeartOfGold();
RepositoryImportResource.RepositoryImportDto repositoryImportDto = new RepositoryImportResource.RepositoryImportDto(); RepositoryImportResource.RepositoryImportDto repositoryImportDto = new RepositoryImportResource.RepositoryImportDto();
repositoryImportDto.setUrl("https://scm-manager.org/scm/repo/scmadmin/scm-manager.git"); repositoryImportDto.setImportUrl("https://scm-manager.org/scm/repo/scmadmin/scm-manager.git");
repositoryImportDto.setNamespace("scmadmin"); repositoryImportDto.setNamespace("scmadmin");
repositoryImportDto.setName("scm-manager"); repositoryImportDto.setName("scm-manager");
@@ -536,7 +536,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
Repository repository = RepositoryTestData.createHeartOfGold(); Repository repository = RepositoryTestData.createHeartOfGold();
RepositoryImportResource.RepositoryImportDto repositoryImportDto = new RepositoryImportResource.RepositoryImportDto(); RepositoryImportResource.RepositoryImportDto repositoryImportDto = new RepositoryImportResource.RepositoryImportDto();
repositoryImportDto.setUrl("https://scm-manager.org/scm/repo/scmadmin/scm-manager.git"); repositoryImportDto.setImportUrl("https://scm-manager.org/scm/repo/scmadmin/scm-manager.git");
repositoryImportDto.setNamespace("scmadmin"); repositoryImportDto.setNamespace("scmadmin");
repositoryImportDto.setName("scm-manager"); repositoryImportDto.setName("scm-manager");
repositoryImportDto.setUsername("trillian"); repositoryImportDto.setUsername("trillian");
@@ -557,7 +557,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
Repository repository = RepositoryTestData.createHeartOfGold(); Repository repository = RepositoryTestData.createHeartOfGold();
RepositoryImportResource.RepositoryImportDto repositoryImportDto = new RepositoryImportResource.RepositoryImportDto(); RepositoryImportResource.RepositoryImportDto repositoryImportDto = new RepositoryImportResource.RepositoryImportDto();
repositoryImportDto.setUrl("https://scm-manager.org/scm/repo/scmadmin/scm-manager.git"); repositoryImportDto.setImportUrl("https://scm-manager.org/scm/repo/scmadmin/scm-manager.git");
repositoryImportDto.setNamespace("scmadmin"); repositoryImportDto.setNamespace("scmadmin");
repositoryImportDto.setName("scm-manager"); repositoryImportDto.setName("scm-manager");

View File

@@ -1,7 +1,7 @@
{ {
"namespace": "hitchhiker", "namespace": "hitchhiker",
"name": "HeartOfGold", "name": "HeartOfGold",
"url": "https://scm-manager-org/scm/repo/secret/puzzle42", "importUrl": "https://scm-manager-org/scm/repo/secret/puzzle42",
"username": "trillian", "username": "trillian",
"password": "secret" "password": "secret"
} }

View File

@@ -1,5 +1,5 @@
{ {
"namespace": "hitchhiker", "namespace": "hitchhiker",
"name": "HeartOfGold", "name": "HeartOfGold",
"url": "https://scm-manager-org/scm/repo/secret/puzzle42" "importUrl": "https://scm-manager-org/scm/repo/secret/puzzle42"
} }