mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 06:25:45 +01:00
refactor UI
This commit is contained in:
@@ -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);
|
||||||
|
};
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
98
scm-ui/ui-webapp/src/repos/components/ImportFromUrlForm.tsx
Normal file
98
scm-ui/ui-webapp/src/repos/components/ImportFromUrlForm.tsx
Normal 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;
|
||||||
@@ -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;
|
||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|||||||
117
scm-ui/ui-webapp/src/repos/components/NamespaceAndNameFields.tsx
Normal file
117
scm-ui/ui-webapp/src/repos/components/NamespaceAndNameFields.tsx
Normal 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);
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
||||||
@@ -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>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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()}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user