Skip to content

Commit

Permalink
adding import repo modal (#19073)
Browse files Browse the repository at this point in the history
* adding import repo modal

* fix copy

* remove name requirement as it's defaulted

* upadte query cache after create
  • Loading branch information
selfcontained authored Nov 15, 2023
1 parent 47fccf3 commit 32aa1bb
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,40 @@ export const getConfigurationQueryKey = (configurationId: string) => {

return key;
};

export type CreateConfigurationArgs = {
name: string;
cloneUrl: string;
};

export const useCreateConfiguration = () => {
const { data: org } = useCurrentOrg();
const queryClient = useQueryClient();

return useMutation<Configuration, Error, CreateConfigurationArgs>({
mutationFn: async ({ name, cloneUrl }) => {
if (!org) {
throw new Error("No org currently selected");
}

// TODO: Should we push this into the api?
// ensure a .git suffix
const normalizedCloneURL = cloneUrl.endsWith(".git") ? cloneUrl : `${cloneUrl}.git`;

const response = await configurationClient.createConfiguration({
name,
cloneUrl: normalizedCloneURL,
organizationId: org.id,
});
if (!response.configuration) {
throw new Error("Failed to create configuration");
}

return response.configuration;
},
onSuccess: (configuration) => {
queryClient.setQueryData(getConfigurationQueryKey(configuration.id), configuration);
queryClient.invalidateQueries({ queryKey: ["configurations", "list"] });
},
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/

import { FC, useCallback, useState } from "react";
import Modal, { ModalBody, ModalFooter, ModalFooterAlert, ModalHeader } from "../../components/Modal";
import { SuggestedRepository } from "@gitpod/gitpod-protocol";
import RepositoryFinder from "../../components/RepositoryFinder";
import { InputField } from "../../components/forms/InputField";
import { AuthorizeGit, useNeedsGitAuthorization } from "../../components/AuthorizeGit";
import { useTemporaryState } from "../../hooks/use-temporary-value";
import { CreateConfigurationArgs, useCreateConfiguration } from "../../data/configurations/configuration-queries";
import type { Configuration } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb";
import { LoadingButton } from "@podkit/buttons/LoadingButton";
import { Button } from "@podkit/buttons/Button";

type Props = {
onCreated: (configuration: Configuration) => void;
onClose: () => void;
};

export const ImportRepositoryModal: FC<Props> = ({ onClose, onCreated }) => {
const needsGitAuth = useNeedsGitAuthorization();
const [selectedRepo, setSelectedRepo] = useState<SuggestedRepository>();
const createConfiguration = useCreateConfiguration();
const [createErrorMsg, setCreateErrorMsg] = useTemporaryState("", 3000);

const handleSubmit = useCallback(() => {
if (!selectedRepo) {
setCreateErrorMsg("Please select a repository");
return;
}

const newProjectArgs: CreateConfigurationArgs = {
// leave the name empty to let the backend generate the name
name: "",
cloneUrl: selectedRepo.url,
};

createConfiguration.mutate(newProjectArgs, {
onSuccess: onCreated,
});
}, [createConfiguration, onCreated, selectedRepo, setCreateErrorMsg]);

const errorMessage =
createErrorMsg ||
(createConfiguration.isError &&
(createConfiguration.error?.message ?? "There was a problem importing your repository"));

return (
<Modal visible onClose={onClose} onSubmit={handleSubmit}>
<ModalHeader>Import repository</ModalHeader>
<ModalBody>
<div className="w-112 max-w-full">
{needsGitAuth ? (
<AuthorizeGit />
) : (
<>
<InputField className="mb-8 w-full">
<RepositoryFinder
selectedContextURL={selectedRepo?.url}
selectedProjectID={selectedRepo?.projectId}
onChange={setSelectedRepo}
excludeProjects
/>
</InputField>
</>
)}
</div>
</ModalBody>
<ModalFooter
alert={
errorMessage && (
<ModalFooterAlert type="danger" onClose={() => setCreateErrorMsg("")}>
{errorMessage}
</ModalFooterAlert>
)
}
>
<Button variant="secondary" onClick={onClose}>
Cancel
</Button>
<LoadingButton type="submit" loading={createConfiguration.isLoading}>
Import
</LoadingButton>
</ModalFooter>
</Modal>
);
};
15 changes: 9 additions & 6 deletions components/dashboard/src/repositories/list/RepositoryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
import { FC, useCallback, useEffect, useState } from "react";
import { LoaderIcon } from "lucide-react";
import { useHistory } from "react-router-dom";
import { Project } from "@gitpod/gitpod-protocol";
import { CreateProjectModal } from "../../projects/create-project-modal/CreateProjectModal";
import { RepositoryListItem } from "./RepoListItem";
import { useListConfigurations } from "../../data/configurations/configuration-queries";
import { useStateWithDebounce } from "../../hooks/use-state-with-debounce";
Expand All @@ -19,6 +17,8 @@ import { Button } from "@podkit/buttons/Button";
import { useDocumentTitle } from "../../hooks/use-document-title";
import { PaginationControls, PaginationCountText } from "./PaginationControls";
import { Table, TableBody, TableHead, TableHeader, TableRow } from "@podkit/tables/Table";
import { ImportRepositoryModal } from "../create/ImportRepositoryModal";
import type { Configuration } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb";

const RepositoryListPage: FC = () => {
useDocumentTitle("Imported repositories");
Expand Down Expand Up @@ -51,9 +51,9 @@ const RepositoryListPage: FC = () => {
const totalRows = data?.pagination?.total ?? 0;
const totalPages = Math.ceil(totalRows / pageSize);

const handleProjectCreated = useCallback(
(project: Project) => {
history.push(`/repositories/${project.id}`);
const handleRepoImported = useCallback(
(configuration: Configuration) => {
history.push(`/repositories/${configuration.id}`);
},
[history],
);
Expand Down Expand Up @@ -145,7 +145,10 @@ const RepositoryListPage: FC = () => {
</div>

{showCreateProjectModal && (
<CreateProjectModal onClose={() => setShowCreateProjectModal(false)} onCreated={handleProjectCreated} />
<ImportRepositoryModal
onClose={() => setShowCreateProjectModal(false)}
onCreated={handleRepoImported}
/>
)}
</>
);
Expand Down
3 changes: 0 additions & 3 deletions components/server/src/api/configuration-service-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ export class ConfigurationServiceAPI implements ServiceImpl<typeof Configuration
if (!req.cloneUrl) {
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "clone_url is required");
}
if (!req.name) {
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "name is required");
}

const project = await this.projectService.createProject(
{
Expand Down

0 comments on commit 32aa1bb

Please sign in to comment.