From ef89d695772735458bcafb9ba5d7178eea2d1700 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Thu, 1 Feb 2024 18:10:15 +0530 Subject: [PATCH] Implement creating project by importing repository (#49) * Implement create project with import repository * Add button for creating project in deploy step --- packages/backend/src/database.ts | 21 +++++ packages/backend/src/entity/Project.ts | 13 ++- packages/backend/src/resolvers.ts | 10 ++ packages/backend/src/schema.gql | 74 ++++++++------- packages/frontend/src/App.tsx | 7 +- .../projects/create/ConnectAccount.tsx | 4 +- .../src/components/projects/create/Deploy.tsx | 76 ++++++++++++++++ .../{template/deploy => }/DeployStep.tsx | 6 +- .../projects/create/ProjectRepoCard.tsx | 52 +++++------ .../projects/create/RepositoryList.tsx | 16 +--- .../frontend/src/context/OctokitContext.tsx | 91 +++++++++++++++++++ .../src/pages/projects/create/Import.tsx | 70 ++++++++++++++ .../src/pages/projects/create/Success.tsx | 1 + .../src/pages/projects/create/Template.tsx | 2 + .../src/pages/projects/create/index.tsx | 65 ++----------- .../src/pages/projects/create/routes.tsx | 5 + .../pages/projects/create/template/Deploy.tsx | 79 +--------------- .../pages/projects/create/template/index.tsx | 2 + packages/frontend/src/types/project.ts | 1 + packages/gql-client/src/client.ts | 15 ++- packages/gql-client/src/mutations.ts | 5 + packages/gql-client/src/types.ts | 11 +++ 22 files changed, 404 insertions(+), 222 deletions(-) create mode 100644 packages/frontend/src/components/projects/create/Deploy.tsx rename packages/frontend/src/components/projects/create/{template/deploy => }/DeployStep.tsx (91%) create mode 100644 packages/frontend/src/context/OctokitContext.tsx create mode 100644 packages/frontend/src/pages/projects/create/Import.tsx diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index 74028f8e..5a9a4bfd 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -326,6 +326,27 @@ export class Database { } } + async addProject (userId: string, projectDetails: DeepPartial): Promise { + const projectRepository = this.dataSource.getRepository(Project); + + // TODO: Check if organization exists + const newProject = projectRepository.create(projectDetails); + // TODO: Set default empty array for webhooks in TypeORM + newProject.webhooks = []; + // TODO: Set icon according to framework + newProject.icon = ''; + + newProject.owner = Object.assign(new User(), { + id: Number(userId) + }); + + newProject.organization = Object.assign(new Organization(), { + id: Number(projectDetails.organizationId) + }); + + return projectRepository.save(newProject); + } + async updateProjectById (projectId: string, updates: DeepPartial): Promise { const projectRepository = this.dataSource.getRepository(Project); const updateResult = await projectRepository.update({ id: projectId }, updates); diff --git a/packages/backend/src/entity/Project.ts b/packages/backend/src/entity/Project.ts index 5736cd0f..92b89a11 100644 --- a/packages/backend/src/entity/Project.ts +++ b/packages/backend/src/entity/Project.ts @@ -28,6 +28,9 @@ export class Project { @JoinColumn({ name: 'organizationId' }) organization!: Organization | null; + @Column('integer') + organizationId!: number; + @Column('varchar') name!: string; @@ -37,14 +40,14 @@ export class Project { @Column('varchar', { length: 255, default: 'main' }) prodBranch!: string; - @Column('text') + @Column('text', { default: '' }) description!: string; - @Column('varchar') - template!: string; + @Column('varchar', { nullable: true }) + template!: string | null; - @Column('varchar') - framework!: string; + @Column('varchar', { nullable: true }) + framework!: string | null; @Column({ type: 'simple-array' diff --git a/packages/backend/src/resolvers.ts b/packages/backend/src/resolvers.ts index ae17833c..1ee02292 100644 --- a/packages/backend/src/resolvers.ts +++ b/packages/backend/src/resolvers.ts @@ -206,6 +206,16 @@ export const createResolvers = async (db: Database, app: OAuthApp): Promise } }, + addProject: async (_: any, { projectDetails }: { projectDetails: DeepPartial }, context: any) => { + try { + await db.addProject(context.userId, projectDetails); + return true; + } catch (err) { + log(err); + return false; + } + }, + updateProject: async (_: any, { projectId, projectDetails }: { projectId: string, projectDetails: DeepPartial }) => { try { return await db.updateProjectById(projectId, projectDetails); diff --git a/packages/backend/src/schema.gql b/packages/backend/src/schema.gql index 589874a4..de379699 100644 --- a/packages/backend/src/schema.gql +++ b/packages/backend/src/schema.gql @@ -64,7 +64,7 @@ type Project { prodBranch: String! description: String template: String - framework: String! + framework: String webhooks: [String!] members: [ProjectMember!] environmentVariables: [EnvironmentVariable!] @@ -116,48 +116,23 @@ type EnvironmentVariable { updatedAt: String! } -type Query { - user: User! - organizations: [Organization!] - projects: [Project!] - projectsInOrganization(organizationId: String!): [Project!] - project(projectId: String!): Project - deployments(projectId: String!): [Deployment!] - environmentVariables(projectId: String!): [EnvironmentVariable!] - projectMembers(projectId: String!): [ProjectMember!] - searchProjects(searchText: String!): [Project!] - domains(projectId: String!): [Domain!] -} - type AuthResult { token: String! } -type Mutation { - removeProjectMember(projectMemberId: String!): Boolean! - updateProjectMember(projectMemberId: String!, data: UpdateProjectMemberInput): Boolean! - addProjectMember(projectId: String!, data: AddProjectMemberInput): Boolean! - addEnvironmentVariables(projectId: String!, environmentVariables: [AddEnvironmentVariableInput!]): Boolean! - removeEnvironmentVariable(environmentVariableId: String!): Boolean! - updateEnvironmentVariable(environmentVariableId: String!, environmentVariable: UpdateEnvironmentVariableInput!): Boolean! - updateDeploymentToProd(deploymentId: String!): Boolean! - updateProject(projectId: String!, projectDetails: UpdateProjectInput): Boolean! - redeployToProd(deploymentId: String!): Boolean! - deleteProject(projectId: String!): Boolean! - deleteDomain(domainId: String!): Boolean! - rollbackDeployment(projectId: String!, deploymentId: String!): Boolean! - addDomain(projectId: String!, domainDetails: AddDomainInput!): Boolean! - updateDomain(domainId: String!, domainDetails: UpdateDomainInput!): Boolean! - authenticateGitHub(code: String!): AuthResult! - unauthenticateGitHub: Boolean! -} - input AddEnvironmentVariableInput { environments: [Environment!]! key: String! value: String! } +input AddProjectInput { + organizationId: String! + name: String! + repository: String! + prodBranch: String! +} + input UpdateProjectInput { name: String description: String @@ -188,3 +163,36 @@ input AddProjectMemberInput { input UpdateProjectMemberInput { permissions: [Permission] } + +type Query { + user: User! + organizations: [Organization!] + projects: [Project!] + projectsInOrganization(organizationId: String!): [Project!] + project(projectId: String!): Project + deployments(projectId: String!): [Deployment!] + environmentVariables(projectId: String!): [EnvironmentVariable!] + projectMembers(projectId: String!): [ProjectMember!] + searchProjects(searchText: String!): [Project!] + domains(projectId: String!): [Domain!] +} + +type Mutation { + addProjectMember(projectId: String!, data: AddProjectMemberInput): Boolean! + updateProjectMember(projectMemberId: String!, data: UpdateProjectMemberInput): Boolean! + removeProjectMember(projectMemberId: String!): Boolean! + addEnvironmentVariables(projectId: String!, environmentVariables: [AddEnvironmentVariableInput!]): Boolean! + updateEnvironmentVariable(environmentVariableId: String!, environmentVariable: UpdateEnvironmentVariableInput!): Boolean! + removeEnvironmentVariable(environmentVariableId: String!): Boolean! + updateDeploymentToProd(deploymentId: String!): Boolean! + addProject(projectDetails: AddProjectInput): Boolean! + updateProject(projectId: String!, projectDetails: UpdateProjectInput): Boolean! + redeployToProd(deploymentId: String!): Boolean! + deleteProject(projectId: String!): Boolean! + deleteDomain(domainId: String!): Boolean! + rollbackDeployment(projectId: String!, deploymentId: String!): Boolean! + addDomain(projectId: String!, domainDetails: AddDomainInput!): Boolean! + updateDomain(domainId: String!, domainDetails: UpdateDomainInput!): Boolean! + authenticateGitHub(code: String!): AuthResult! + unauthenticateGitHub: Boolean! +} diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 78908393..e140b823 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -9,6 +9,7 @@ import { projectsRoutesWithoutSearch, } from './pages/projects/routes'; import ProjectSearchLayout from './layouts/ProjectSearch'; +import { OctokitProvider } from './context/OctokitContext'; const router = createBrowserRouter([ { @@ -40,7 +41,11 @@ const router = createBrowserRouter([ ]); function App() { - return ; + return ( + + + + ); } export default App; diff --git a/packages/frontend/src/components/projects/create/ConnectAccount.tsx b/packages/frontend/src/components/projects/create/ConnectAccount.tsx index 02187a56..0864199c 100644 --- a/packages/frontend/src/components/projects/create/ConnectAccount.tsx +++ b/packages/frontend/src/components/projects/create/ConnectAccount.tsx @@ -10,10 +10,10 @@ const GITHUB_OAUTH_URL = `https://github.com/login/oauth/authorize?client_id=${ }&scope=${encodeURIComponent(SCOPES)}`; interface ConnectAccountInterface { - onToken: (token: string) => void; + onAuth: (token: string) => void; } -const ConnectAccount = ({ onToken }: ConnectAccountInterface) => { +const ConnectAccount = ({ onAuth: onToken }: ConnectAccountInterface) => { const client = useGQLClient(); const handleCode = async (code: string) => { diff --git a/packages/frontend/src/components/projects/create/Deploy.tsx b/packages/frontend/src/components/projects/create/Deploy.tsx new file mode 100644 index 00000000..6122328d --- /dev/null +++ b/packages/frontend/src/components/projects/create/Deploy.tsx @@ -0,0 +1,76 @@ +import React, { useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { Button, Typography } from '@material-tailwind/react'; + +import { DeployStep, DeployStatus } from './DeployStep'; +import { Stopwatch, setStopWatchOffset } from '../../StopWatch'; +import ConfirmDialog from '../../shared/ConfirmDialog'; + +const Deploy = () => { + const [open, setOpen] = React.useState(false); + const handleOpen = () => setOpen(!open); + const navigate = useNavigate(); + + const handleCancel = useCallback(() => { + navigate('/projects/create'); + }, []); + + return ( +
+
+
+

Deployment started ...

+
+ ^  + +
+
+
+ +
+ + + This will halt the deployment and you will have to start the process + from scratch. + + +
+ + + + +
+ ); +}; + +export default Deploy; diff --git a/packages/frontend/src/components/projects/create/template/deploy/DeployStep.tsx b/packages/frontend/src/components/projects/create/DeployStep.tsx similarity index 91% rename from packages/frontend/src/components/projects/create/template/deploy/DeployStep.tsx rename to packages/frontend/src/components/projects/create/DeployStep.tsx index f6bccee3..ce466b32 100644 --- a/packages/frontend/src/components/projects/create/template/deploy/DeployStep.tsx +++ b/packages/frontend/src/components/projects/create/DeployStep.tsx @@ -3,9 +3,9 @@ import toast from 'react-hot-toast'; import { Collapse, Button, Typography } from '@material-tailwind/react'; -import { Stopwatch, setStopWatchOffset } from '../../../../StopWatch'; -import FormatMillisecond from '../../../../FormatMilliSecond'; -import processLogs from '../../../../../assets/process-logs.json'; +import { Stopwatch, setStopWatchOffset } from '../../StopWatch'; +import FormatMillisecond from '../../FormatMilliSecond'; +import processLogs from '../../../assets/process-logs.json'; enum DeployStatus { PROCESSING = 'progress', diff --git a/packages/frontend/src/components/projects/create/ProjectRepoCard.tsx b/packages/frontend/src/components/projects/create/ProjectRepoCard.tsx index 38e4da89..d5716231 100644 --- a/packages/frontend/src/components/projects/create/ProjectRepoCard.tsx +++ b/packages/frontend/src/components/projects/create/ProjectRepoCard.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Link } from 'react-router-dom'; import { Chip, IconButton } from '@material-tailwind/react'; @@ -7,39 +8,36 @@ import { GitRepositoryDetails } from '../../../types/project'; interface ProjectRepoCardProps { repository: GitRepositoryDetails; - onClick: () => void; } -const ProjectRepoCard: React.FC = ({ - repository, - onClick, -}) => { +const ProjectRepoCard: React.FC = ({ repository }) => { return ( -
-
^
-
-
- {repository.full_name} - {repository.visibility === 'private' ? ( - - ) : ( - '' - )} +
+
^
+
+
+ {repository.full_name} + {repository.visibility === 'private' ? ( + + ) : ( + '' + )} +
+

{repository.updated_at && relativeTime(repository.updated_at)}

+
+
+ {'>'}
-

{repository.updated_at && relativeTime(repository.updated_at)}

-
- {'>'} -
-
+ ); }; diff --git a/packages/frontend/src/components/projects/create/RepositoryList.tsx b/packages/frontend/src/components/projects/create/RepositoryList.tsx index 6c5c5049..5b4d7526 100644 --- a/packages/frontend/src/components/projects/create/RepositoryList.tsx +++ b/packages/frontend/src/components/projects/create/RepositoryList.tsx @@ -13,14 +13,10 @@ const DEFAULT_SEARCHED_REPO = ''; const REPOS_PER_PAGE = 5; interface RepositoryListProps { - repoSelectionHandler: (repo: GitRepositoryDetails) => void; octokit: Octokit; } -const RepositoryList = ({ - repoSelectionHandler, - octokit, -}: RepositoryListProps) => { +const RepositoryList = ({ octokit }: RepositoryListProps) => { const [searchedRepo, setSearchedRepo] = useState(DEFAULT_SEARCHED_REPO); const [selectedAccount, setSelectedAccount] = useState(''); const [orgs, setOrgs] = useState([]); @@ -135,15 +131,7 @@ const RepositoryList = ({
{Boolean(repositoryDetails.length) ? ( repositoryDetails.map((repo, key) => { - return ( - { - repoSelectionHandler(repo); - }} - /> - ); + return ; }) ) : (
diff --git a/packages/frontend/src/context/OctokitContext.tsx b/packages/frontend/src/context/OctokitContext.tsx new file mode 100644 index 00000000..21c88b2e --- /dev/null +++ b/packages/frontend/src/context/OctokitContext.tsx @@ -0,0 +1,91 @@ +import React, { + createContext, + useContext, + ReactNode, + useState, + useMemo, + useCallback, + useEffect, +} from 'react'; +import { Octokit, RequestError } from 'octokit'; + +import { useGQLClient } from './GQLClientContext'; + +const UNAUTHORIZED_ERROR_CODE = 401; + +interface ContextValue { + octokit: Octokit | null; + updateAuth: () => void; +} + +const OctokitContext = createContext({ + octokit: null, + updateAuth: () => {}, +}); + +export const OctokitProvider = ({ children }: { children: ReactNode }) => { + const [authToken, setAuthToken] = useState(''); + const client = useGQLClient(); + + const fetchUser = useCallback(async () => { + const { user } = await client.getUser(); + + if (user.gitHubToken) { + setAuthToken(user.gitHubToken); + } + }, []); + + const updateAuth = useCallback(() => { + fetchUser(); + }, []); + + const octokit = useMemo(() => { + if (!authToken) { + return null; + } + + return new Octokit({ auth: authToken }); + }, [authToken]); + + useEffect(() => { + fetchUser(); + }, []); + + useEffect(() => { + if (!octokit) { + return; + } + + // TODO: Handle React component error + const interceptor = async (error: RequestError | Error) => { + if ( + error instanceof RequestError && + error.status === UNAUTHORIZED_ERROR_CODE + ) { + await client.unauthenticateGithub(); + await fetchUser(); + } + + throw error; + }; + + octokit.hook.error('request', interceptor); + + return () => { + // Remove the interceptor when the component unmounts + octokit.hook.remove('request', interceptor); + }; + }, [octokit, client]); + + return ( + + {children} + + ); +}; + +export const useOctokit = () => { + const { octokit, updateAuth } = useContext(OctokitContext); + + return { octokit, updateAuth }; +}; diff --git a/packages/frontend/src/pages/projects/create/Import.tsx b/packages/frontend/src/pages/projects/create/Import.tsx new file mode 100644 index 00000000..d8f0b1c3 --- /dev/null +++ b/packages/frontend/src/pages/projects/create/Import.tsx @@ -0,0 +1,70 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { useNavigate, useSearchParams } from 'react-router-dom'; + +import { Button } from '@material-tailwind/react'; + +import { useOctokit } from '../../../context/OctokitContext'; +import { GitRepositoryDetails } from '../../../types/project'; +import Deploy from '../../../components/projects/create/Deploy'; +import { useGQLClient } from '../../../context/GQLClientContext'; + +const Import = () => { + const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + const { octokit } = useOctokit(); + const client = useGQLClient(); + const [gitRepo, setGitRepo] = useState(); + + useEffect(() => { + const fetchRepo = async () => { + if (!octokit) { + return; + } + + const result = await octokit.rest.repos.get({ + owner: searchParams.get('owner') ?? '', + repo: searchParams.get('repo') ?? '', + }); + + setGitRepo(result.data); + }; + + fetchRepo(); + }, [searchParams, octokit]); + + const createProjectAndCreate = useCallback(async () => { + if (!gitRepo) { + return; + } + + const { addProject } = await client.addProject({ + // TODO: Implement form for setting project name + name: gitRepo.name, + // TODO: Get organization id from context or URL + organizationId: String(1), + prodBranch: gitRepo.default_branch ?? 'main', + repository: gitRepo.full_name, + }); + + if (addProject) { + navigate('/projects/create/success'); + } + }, [client, gitRepo]); + + return ( +
+
+
^
+
{gitRepo?.full_name}
+
+ + + + +
+ ); +}; + +export default Import; diff --git a/packages/frontend/src/pages/projects/create/Success.tsx b/packages/frontend/src/pages/projects/create/Success.tsx index d2fdd03b..e416a98a 100644 --- a/packages/frontend/src/pages/projects/create/Success.tsx +++ b/packages/frontend/src/pages/projects/create/Success.tsx @@ -3,6 +3,7 @@ import { Link } from 'react-router-dom'; import { Button } from '@material-tailwind/react'; +// TODO: Use dynamic route params for fetching project created details const Success = () => { return (
diff --git a/packages/frontend/src/pages/projects/create/Template.tsx b/packages/frontend/src/pages/projects/create/Template.tsx index 544e700c..04f5bbc5 100644 --- a/packages/frontend/src/pages/projects/create/Template.tsx +++ b/packages/frontend/src/pages/projects/create/Template.tsx @@ -8,6 +8,7 @@ const STEPPER_VALUES = [ { step: 2, route: '/projects/create/template/deploy', label: 'Deploy' }, ]; +// TODO: Set dynamic route for template and load details from DB const CreateWithTemplate = () => { const location = useLocation(); @@ -23,6 +24,7 @@ const CreateWithTemplate = () => {
^
React native
+ {/* TODO: Get template Git link from DB */}
^snowball-tools/react-native-starter
diff --git a/packages/frontend/src/pages/projects/create/index.tsx b/packages/frontend/src/pages/projects/create/index.tsx index d4c99dcc..1f76fef6 100644 --- a/packages/frontend/src/pages/projects/create/index.tsx +++ b/packages/frontend/src/pages/projects/create/index.tsx @@ -1,66 +1,13 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { User } from 'gql-client'; -import { Octokit, RequestError } from 'octokit'; +import React from 'react'; import templateDetails from '../../../assets/templates.json'; import TemplateCard from '../../../components/projects/create/TemplateCard'; import RepositoryList from '../../../components/projects/create/RepositoryList'; import ConnectAccount from '../../../components/projects/create/ConnectAccount'; -import { useGQLClient } from '../../../context/GQLClientContext'; - -const UNAUTHORIZED_ERROR_CODE = 401; +import { useOctokit } from '../../../context/OctokitContext'; const NewProject = () => { - const client = useGQLClient(); - const [user, setUser] = useState(); - - const octokit = useMemo(() => { - if (!user?.gitHubToken) { - return; - } - - // TODO: Create github/octokit context - return new Octokit({ auth: user.gitHubToken }); - }, [user]); - - const fetchUser = useCallback(async () => { - const { user } = await client.getUser(); - setUser(user); - }, []); - - const handleToken = useCallback(() => { - fetchUser(); - }, []); - - useEffect(() => { - fetchUser(); - }, []); - - useEffect(() => { - if (!octokit) { - return; - } - - // TODO: Handle React component error - const interceptor = async (error: RequestError | Error) => { - if ( - error instanceof RequestError && - error.status === UNAUTHORIZED_ERROR_CODE - ) { - await client.unauthenticateGithub(); - await fetchUser(); - } - - throw error; - }; - - octokit.hook.error('request', interceptor); - - return () => { - // Remove the interceptor when the component unmounts - octokit.hook.remove('request', interceptor); - }; - }, [octokit, client]); + const { octokit, updateAuth } = useOctokit(); return ( <> @@ -69,7 +16,7 @@ const NewProject = () => { {templateDetails.map((framework, key) => { return ( @@ -78,9 +25,9 @@ const NewProject = () => {
Import a repository
{Boolean(octokit) ? ( - {}} /> + ) : ( - + )} ); diff --git a/packages/frontend/src/pages/projects/create/routes.tsx b/packages/frontend/src/pages/projects/create/routes.tsx index 214450a2..03fc1154 100644 --- a/packages/frontend/src/pages/projects/create/routes.tsx +++ b/packages/frontend/src/pages/projects/create/routes.tsx @@ -4,6 +4,7 @@ import NewProject from './index'; import CreateWithTemplate from './Template'; import { templateRoutes } from './template/routes'; import Success from './Success'; +import Import from './Import'; export const createProjectRoutes = [ { @@ -19,4 +20,8 @@ export const createProjectRoutes = [ path: 'success', element: , }, + { + path: 'import', + element: , + }, ]; diff --git a/packages/frontend/src/pages/projects/create/template/Deploy.tsx b/packages/frontend/src/pages/projects/create/template/Deploy.tsx index 6c38ef78..d8a21c5c 100644 --- a/packages/frontend/src/pages/projects/create/template/Deploy.tsx +++ b/packages/frontend/src/pages/projects/create/template/Deploy.tsx @@ -1,82 +1,9 @@ -import React, { useCallback } from 'react'; -import { useNavigate } from 'react-router-dom'; +import React from 'react'; -import { Button, Typography } from '@material-tailwind/react'; - -import { - DeployStep, - DeployStatus, -} from '../../../../components/projects/create/template/deploy/DeployStep'; -import { - Stopwatch, - setStopWatchOffset, -} from '../../../../components/StopWatch'; -import ConfirmDialog from '../../../../components/shared/ConfirmDialog'; +import DeployComponent from '../../../../components/projects/create/Deploy'; const Deploy = () => { - const [open, setOpen] = React.useState(false); - const handleOpen = () => setOpen(!open); - const navigate = useNavigate(); - - const handleCancel = useCallback(() => { - navigate('/projects/create/template'); - }, []); - - return ( -
-
-
-

Deployment started ...

-
- ^  - -
-
-
- -
- - - This will halt the deployment and you will have to start the process - from scratch. - - -
- - - - -
- ); + return ; }; export default Deploy; diff --git a/packages/frontend/src/pages/projects/create/template/index.tsx b/packages/frontend/src/pages/projects/create/template/index.tsx index ca97f019..795e208d 100644 --- a/packages/frontend/src/pages/projects/create/template/index.tsx +++ b/packages/frontend/src/pages/projects/create/template/index.tsx @@ -22,6 +22,8 @@ const CreateRepo = () => { }, }); + // TODO: Get users and orgs from GitHub + return (
{})}>
diff --git a/packages/frontend/src/types/project.ts b/packages/frontend/src/types/project.ts index 7232eb5b..8a6fca75 100644 --- a/packages/frontend/src/types/project.ts +++ b/packages/frontend/src/types/project.ts @@ -41,6 +41,7 @@ export interface GitRepositoryDetails { owner: GitOrgDetails | null; visibility?: string; updated_at?: string | null; + default_branch?: string; } export enum GitSelect { diff --git a/packages/gql-client/src/client.ts b/packages/gql-client/src/client.ts index 34250bd0..4cc547ff 100644 --- a/packages/gql-client/src/client.ts +++ b/packages/gql-client/src/client.ts @@ -1,8 +1,8 @@ import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client'; import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject, getDomains, getProjectsInOrganization } from './queries'; -import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse, AuthenticateGitHubResponse, UnauthenticateGitHubResponse, UpdateEnvironmentVariableResponse, UpdateEnvironmentVariableInput, RemoveEnvironmentVariableResponse, UpdateProjectMemberInput, RemoveProjectMemberResponse, UpdateProjectMemberResponse, DeleteDomainResponse, AddProjectMemberInput, AddProjectMemberResponse } from './types'; -import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain, addProjectMember } from './mutations'; +import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse, AuthenticateGitHubResponse, UnauthenticateGitHubResponse, UpdateEnvironmentVariableResponse, UpdateEnvironmentVariableInput, RemoveEnvironmentVariableResponse, UpdateProjectMemberInput, RemoveProjectMemberResponse, UpdateProjectMemberResponse, DeleteDomainResponse, AddProjectMemberInput, AddProjectMemberResponse, AddProjectInput, AddProjectResponse } from './types'; +import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain, addProjectMember, addProject } from './mutations'; export interface GraphQLConfig { gqlEndpoint: string; @@ -194,6 +194,17 @@ export class GQLClient { return data; } + async addProject (projectDetails: AddProjectInput): Promise { + const { data } = await this.client.mutate({ + mutation: addProject, + variables: { + projectDetails + } + }); + + return data; + } + async updateProject (projectId: string, projectDetails: UpdateProjectInput): Promise { const { data } = await this.client.mutate({ mutation: updateProjectMutation, diff --git a/packages/gql-client/src/mutations.ts b/packages/gql-client/src/mutations.ts index f3ff6afe..646d33e0 100644 --- a/packages/gql-client/src/mutations.ts +++ b/packages/gql-client/src/mutations.ts @@ -42,6 +42,11 @@ mutation ($deploymentId: String!) { } `; +export const addProject = gql` +mutation ($projectDetails: AddProjectInput) { + addProject(projectDetails: $projectDetails) +}`; + export const updateProjectMutation = gql` mutation ($projectId: String!, $projectDetails: UpdateProjectInput) { updateProject(projectId: $projectId, projectDetails: $projectDetails) diff --git a/packages/gql-client/src/types.ts b/packages/gql-client/src/types.ts index 019cb52f..06d9b140 100644 --- a/packages/gql-client/src/types.ts +++ b/packages/gql-client/src/types.ts @@ -217,6 +217,10 @@ export type UpdateDeploymentToProdResponse = { updateDeploymentToProd: boolean; } +export type AddProjectResponse = { + addProject: boolean +} + export type UpdateProjectResponse = { updateProject: boolean; } @@ -233,6 +237,13 @@ export type DeleteDomainResponse = { deleteDomain: boolean; } +export type AddProjectInput = { + organizationId: string; + name: string; + repository: string; + prodBranch: string; +} + export type UpdateProjectInput = { name?: string description?: string