Add GQL mutation for updating domain (#50)

* Pass data for all domains in edit domain dialog box

* Add mutation to update domain by id

* implement front end and gql client method to edit domain

* Rename arguments of resolver function to update domain and project

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2024-01-30 14:01:09 +05:30 committed by Ashwin Phatak
parent a34e2286a6
commit fdf06f9bd0
9 changed files with 111 additions and 28 deletions

View File

@ -365,4 +365,16 @@ export class Database {
return domains;
}
async updateDomainById (domainId: string, updates: DeepPartial<Domain>): Promise<boolean> {
const domainRepository = this.dataSource.getRepository(Domain);
const updateResult = await domainRepository.update({ id: Number(domainId) }, updates);
if (updateResult.affected) {
return updateResult.affected > 0;
} else {
return false;
}
}
}

View File

@ -153,9 +153,9 @@ export const createResolvers = async (db: Database): Promise<any> => {
}
},
updateProject: async (_: any, { projectId, updateProject }: { projectId: string, updateProject: { name: string, description: string } }) => {
updateProject: async (_: any, { projectId, projectDetails }: { projectId: string, projectDetails: { name: string, description: string } }) => {
try {
return db.updateProjectById(projectId, updateProject);
return db.updateProjectById(projectId, projectDetails);
} catch (err) {
log(err);
return false;
@ -198,6 +198,16 @@ export const createResolvers = async (db: Database): Promise<any> => {
log(err);
return false;
}
},
updateDomain: async (_: any, { domainId, domainDetails }: { domainId: string, domainDetails: {name?: string, isRedirected?: boolean, branch?: string }}) => {
try {
await db.updateDomainById(domainId, domainDetails);
return true;
} catch (err) {
log(err);
return false;
}
}
}
};

View File

@ -129,11 +129,12 @@ type Mutation {
removeMember(memberId: String!): Boolean!
addEnvironmentVariables(projectId: String!, environmentVariables: [AddEnvironmentVariableInput!]): Boolean!
updateDeploymentToProd(deploymentId: String!): Boolean!
updateProject(projectId: String!, updateProject: UpdateProjectInput): Boolean!
updateProject(projectId: String!, projectDetails: UpdateProjectInput): Boolean!
redeployToProd(deploymentId: String!): Boolean!
deleteProject(projectId: String!): Boolean!
rollbackDeployment(projectId: String!, deploymentId: String!): Boolean!
addDomain(projectId: String!, domainDetails: AddDomainInput!): Boolean!
updateDomain(domainId: String!, domainDetails: UpdateDomainInput!): Boolean!
}
input AddEnvironmentVariableInput {
@ -150,3 +151,9 @@ input UpdateProjectInput {
input AddDomainInput {
name: String!
}
input UpdateDomainInput {
name: String
isRedirected: Boolean
branch: String
}

View File

@ -24,9 +24,11 @@ enum RefreshStatus {
}
interface DomainCardProps {
domains: Domain[];
domain: Domain;
repo: RepositoryDetails;
project: ProjectDetails;
onUpdate: () => Promise<void>;
}
const CHECK_FAIL_TIMEOUT = 5000; // In milliseconds
@ -38,7 +40,13 @@ const DOMAIN_RECORD = {
value: '56.49.19.21',
};
const DomainCard = ({ domain, repo, project }: DomainCardProps) => {
const DomainCard = ({
domains,
domain,
repo,
project,
onUpdate,
}: DomainCardProps) => {
const [refreshStatus, SetRefreshStatus] = useState(RefreshStatus.IDLE);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [editDialogOpen, setEditDialogOpen] = useState(false);
@ -163,10 +171,11 @@ const DomainCard = ({ domain, repo, project }: DomainCardProps) => {
handleOpen={() => {
setEditDialogOpen((preVal) => !preVal);
}}
domains={domains}
open={editDialogOpen}
domain={domain}
repo={repo}
project={project}
onUpdate={onUpdate}
/>
</>
);

View File

@ -55,10 +55,12 @@ const Domains = () => {
{domains.map((domain) => {
return (
<DomainCard
domains={domains}
domain={domain}
key={domain.id}
repo={linkedRepo!}
project={currentProject!}
onUpdate={fetchDomains}
/>
);
})}

View File

@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { Controller, useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import { Domain } from 'gql-client';
@ -15,25 +15,30 @@ import {
Option,
} from '@material-tailwind/react';
import { ProjectDetails, RepositoryDetails } from '../../../../types/project';
import { RepositoryDetails } from '../../../../types/project';
import { useGQLClient } from '../../../../context/GQLClientContext';
const DEFAULT_REDIRECT_OPTIONS = ['none'];
interface EditDomainDialogProp {
domains: Domain[];
open: boolean;
handleOpen: () => void;
domain: Domain;
repo: RepositoryDetails;
project: ProjectDetails;
onUpdate: () => Promise<void>;
}
const EditDomainDialog = ({
domains,
open,
handleOpen,
domain,
repo,
project,
onUpdate,
}: EditDomainDialogProp) => {
const client = useGQLClient();
const getRedirectUrl = (domain: Domain) => {
const domainArr = domain.name.split('www.');
let redirectUrl = '';
@ -45,12 +50,6 @@ const EditDomainDialog = ({
return redirectUrl;
};
const domains = project.deployments
.filter((deployment: any) => {
return deployment.domain != null;
})
.map((deployment: any) => deployment.domain);
const redirectOptions = useMemo(() => {
const redirectUrl = getRedirectUrl(domain);
return [...DEFAULT_REDIRECT_OPTIONS, redirectUrl];
@ -63,9 +62,31 @@ const EditDomainDialog = ({
(domain) => domain.name === redirectUrl,
);
return domainRedirected?.isRedirectedto;
return domainRedirected?.isRedirected;
}, [domain]);
const onSubmit = useCallback(
async (data: any) => {
const updates = {
name: data.name,
branch: data.branch,
isRedirected: data.redirectedTo !== 'none',
};
const { updateDomain } = await client.updateDomain(domain.id, updates);
if (updateDomain) {
await onUpdate();
toast.success(`Domain ${domain.name} has been updated`);
} else {
toast.error(`Error updating domain ${domain.name}`);
}
handleOpen();
},
[client],
);
const {
handleSubmit,
register,
@ -94,12 +115,7 @@ const EditDomainDialog = ({
X
</Button>
</DialogHeader>
<form
onSubmit={handleSubmit(() => {
handleOpen();
toast.success(`Domain ${domain.name} has been updated`);
})}
>
<form onSubmit={handleSubmit(onSubmit)}>
<DialogBody className="flex flex-col gap-2 p-4">
<Typography variant="small">Domain name</Typography>
<Input crossOrigin={undefined} {...register('name')} />

View File

@ -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, RemoveMemberResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse } from './types';
import { removeMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment } from './mutations';
import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, RemoveMemberResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse } from './types';
import { removeMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation } from './mutations';
export interface GraphQLConfig {
gqlEndpoint: string;
@ -147,12 +147,24 @@ export class GQLClient {
return data;
}
async updateProject (projectId: string, updateProject: UpdateProjectInput): Promise<UpdateProjectResponse> {
async updateProject (projectId: string, projectDetails: UpdateProjectInput): Promise<UpdateProjectResponse> {
const { data } = await this.client.mutate({
mutation: updateProjectMutation,
variables: {
projectId,
updateProject
projectDetails
}
});
return data;
}
async updateDomain (domainId: string, domainDetails: UpdateDomainInput): Promise<UpdateDomainResponse> {
const { data } = await this.client.mutate({
mutation: updateDomainMutation,
variables: {
domainId,
domainDetails
}
});

View File

@ -19,8 +19,13 @@ mutation ($deploymentId: String!) {
`;
export const updateProjectMutation = gql`
mutation ($projectId: String!, $updateProject: UpdateProjectInput) {
updateProject(projectId: $projectId, updateProject: $updateProject)
mutation ($projectId: String!, $projectDetails: UpdateProjectInput) {
updateProject(projectId: $projectId, projectDetails: $projectDetails)
}`;
export const updateDomainMutation = gql`
mutation ($domainId: String!, $domainDetails: UpdateDomainInput!) {
updateDomain(domainId: $domainId, domainDetails: $domainDetails)
}`;
export const redeployToProd = gql`

View File

@ -187,6 +187,10 @@ export type UpdateProjectResponse = {
updateProject: boolean;
}
export type UpdateDomainResponse = {
updateDomain: boolean;
}
export type DeleteProjectResponse = {
deleteProject: boolean;
}
@ -196,6 +200,12 @@ export type UpdateProjectInput = {
description: string
}
export type UpdateDomainInput = {
name?: string;
isRedirected?: boolean;
branch?: string;
}
export type RedeployToProdResponse = {
redeployToProd: boolean
}