mirror of
https://github.com/snowball-tools/snowballtools-base.git
synced 2024-12-22 16:37:44 +00:00
Implement functionality to add and delete Deploy webhooks (#47)
* Add mutation to add webhooks * Implement frontend to add webhooks and refactor code to use updateProject resolver * Implement functionality to delete webhooks * Refactor webhook card component * Update readme for frontend env GitHub OAuth client ID --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
parent
aa49bb0de2
commit
7e2a313012
14
README.md
14
README.md
@ -31,7 +31,7 @@
|
||||
```
|
||||
|
||||
- Set `githubOauth.clientId` and `githubOauth.clientSecret` in backend [config file](packages/backend/environments/local.toml)
|
||||
- Client id and secret will be available after creating Github OAuth app
|
||||
- Client ID and secret will be available after creating Github OAuth app
|
||||
- https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app
|
||||
- In "Homepage URL", type `http://localhost:3000`
|
||||
- In "Authorization callback URL", type `http://localhost:3000/projects/create`
|
||||
@ -43,16 +43,22 @@
|
||||
yarn start
|
||||
```
|
||||
|
||||
- Change directory to `packages/frontend` in a new terminal
|
||||
|
||||
```bash
|
||||
cd packages/frontend
|
||||
```
|
||||
|
||||
- Copy the graphQL endpoint from terminal and add the endpoint in the [.env](packages/frontend/.env) file present in `packages/frontend`
|
||||
|
||||
```
|
||||
REACT_APP_GQL_SERVER_URL = 'http://localhost:8000/graphql'
|
||||
```
|
||||
|
||||
- Change directory to `packages/frontend`
|
||||
- Copy the GitHub OAuth app client ID from previous steps and set it in frontend [.env](packages/frontend/.env) file
|
||||
|
||||
```bash
|
||||
cd packages/frontend
|
||||
```
|
||||
REACT_APP_GITHUB_CLIENT_ID = <CLIENT_ID>
|
||||
```
|
||||
|
||||
- Start the React application
|
||||
|
@ -9,6 +9,7 @@ import { deploymentToGqlType, projectMemberToGqlType, projectToGqlType, environm
|
||||
import { Environment } from './entity/Deployment';
|
||||
import { Permission } from './entity/ProjectMember';
|
||||
import { Domain } from './entity/Domain';
|
||||
import { Project } from './entity/Project';
|
||||
|
||||
const log = debug('snowball:database');
|
||||
|
||||
@ -188,9 +189,9 @@ export const createResolvers = async (db: Database, app: OAuthApp): Promise<any>
|
||||
}
|
||||
},
|
||||
|
||||
updateProject: async (_: any, { projectId, projectDetails }: { projectId: string, projectDetails: { name: string, description: string } }) => {
|
||||
updateProject: async (_: any, { projectId, projectDetails }: { projectId: string, projectDetails: DeepPartial<Project> }) => {
|
||||
try {
|
||||
return db.updateProjectById(projectId, projectDetails);
|
||||
return await db.updateProjectById(projectId, projectDetails);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
@ -273,15 +274,6 @@ export const createResolvers = async (db: Database, app: OAuthApp): Promise<any>
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
updateProdBranch: async (_: any, { projectId, prodBranch }: {projectId: string, prodBranch: string }) => {
|
||||
try {
|
||||
return await db.updateProjectById(projectId, { prodBranch });
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -139,7 +139,6 @@ type Mutation {
|
||||
updateEnvironmentVariable(environmentVariableId: String!, environmentVariable: UpdateEnvironmentVariableInput!): Boolean!
|
||||
updateDeploymentToProd(deploymentId: String!): Boolean!
|
||||
updateProject(projectId: String!, projectDetails: UpdateProjectInput): Boolean!
|
||||
updateProdBranch(projectId: String!, prodBranch: String!): Boolean!
|
||||
redeployToProd(deploymentId: String!): Boolean!
|
||||
deleteProject(projectId: String!): Boolean!
|
||||
deleteDomain(domainId: String!): Boolean!
|
||||
@ -159,6 +158,8 @@ input AddEnvironmentVariableInput {
|
||||
input UpdateProjectInput {
|
||||
name: String
|
||||
description: String
|
||||
prodBranch: String
|
||||
webhooks: [String!]
|
||||
}
|
||||
|
||||
input AddDomainInput {
|
||||
|
@ -1,3 +1,3 @@
|
||||
REACT_APP_GQL_SERVER_URL = 'http://localhost:8000/graphql'
|
||||
|
||||
REACT_APP_GITHUB_CLIENT_ID = 4720362b6740b00652b6
|
||||
REACT_APP_GITHUB_CLIENT_ID =
|
||||
|
@ -17,17 +17,6 @@ const GitTabPanel = ({
|
||||
}) => {
|
||||
const client = useGQLClient();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { isSubmitSuccessful },
|
||||
} = useForm({
|
||||
defaultValues: {
|
||||
webhookUrl: project.webhooks,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
register: registerProdBranch,
|
||||
handleSubmit: handleSubmitProdBranch,
|
||||
@ -40,12 +29,11 @@ const GitTabPanel = ({
|
||||
|
||||
const updateProdBranchHandler = useCallback(
|
||||
async (data: any) => {
|
||||
const { updateProdBranch } = await client.updateProdBranch(
|
||||
project.id,
|
||||
data.prodBranch,
|
||||
);
|
||||
const { updateProject } = await client.updateProject(project.id, {
|
||||
prodBranch: data.prodBranch,
|
||||
});
|
||||
|
||||
if (updateProdBranch) {
|
||||
if (updateProject) {
|
||||
await onUpdate();
|
||||
toast.success('Production branch upadated successfully');
|
||||
} else {
|
||||
@ -55,9 +43,33 @@ const GitTabPanel = ({
|
||||
[project],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
reset();
|
||||
}, [isSubmitSuccessful]);
|
||||
const {
|
||||
register: registerWebhooks,
|
||||
handleSubmit: handleSubmitWebhooks,
|
||||
reset: resetWebhooks,
|
||||
} = useForm({
|
||||
defaultValues: {
|
||||
webhookUrl: '',
|
||||
},
|
||||
});
|
||||
|
||||
const updateWebhooksHandler = useCallback(
|
||||
async (data: any) => {
|
||||
const { updateProject } = await client.updateProject(project.id, {
|
||||
webhooks: [...project.webhooks, data.webhookUrl],
|
||||
});
|
||||
|
||||
if (updateProject) {
|
||||
await onUpdate();
|
||||
toast.success('Webhook added successfully');
|
||||
} else {
|
||||
toast.error('Error adding webhook');
|
||||
}
|
||||
|
||||
resetWebhooks();
|
||||
},
|
||||
[project],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
resetProdBranch({
|
||||
@ -65,8 +77,18 @@ const GitTabPanel = ({
|
||||
});
|
||||
}, [project]);
|
||||
|
||||
const handleDelete = () => {
|
||||
// TODO: Impletement functionality to delete webhooks
|
||||
const handleDeleteWebhook = async (index: number) => {
|
||||
project.webhooks.splice(index, 1);
|
||||
const { updateProject } = await client.updateProject(project.id, {
|
||||
webhooks: project.webhooks,
|
||||
});
|
||||
|
||||
if (updateProject) {
|
||||
await onUpdate();
|
||||
toast.success('Webhook deleted successfully');
|
||||
} else {
|
||||
toast.error('Error deleting webhook');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -117,11 +139,7 @@ const GitTabPanel = ({
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form
|
||||
onSubmit={handleSubmit(() => {
|
||||
toast.success('Webhook added successfully.');
|
||||
})}
|
||||
>
|
||||
<form onSubmit={handleSubmitWebhooks(updateWebhooksHandler)}>
|
||||
<div className="mb-2 p-2">
|
||||
<Typography variant="h6" className="text-black">
|
||||
Deploy webhooks
|
||||
@ -133,7 +151,10 @@ const GitTabPanel = ({
|
||||
<div className="flex gap-1">
|
||||
<div className="grow">
|
||||
<Typography variant="small">Webhook URL</Typography>
|
||||
<Input crossOrigin={undefined} {...register('webhookUrl')} />
|
||||
<Input
|
||||
crossOrigin={undefined}
|
||||
{...registerWebhooks('webhookUrl')}
|
||||
/>
|
||||
</div>
|
||||
<div className="self-end">
|
||||
<Button size="sm" type="submit">
|
||||
@ -147,9 +168,8 @@ const GitTabPanel = ({
|
||||
{project.webhooks.map((webhookUrl, index) => {
|
||||
return (
|
||||
<WebhookCard
|
||||
webhooksArray={project.webhooks}
|
||||
webhookUrl={webhookUrl}
|
||||
handleDelete={() => handleDelete()}
|
||||
onDelete={() => handleDeleteWebhook(index)}
|
||||
key={index}
|
||||
/>
|
||||
);
|
||||
|
@ -5,21 +5,22 @@ import { Button, Typography } from '@material-tailwind/react';
|
||||
|
||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
||||
|
||||
const WebhookCard = (props: {
|
||||
webhooksArray: string[];
|
||||
interface WebhookCardProps {
|
||||
webhookUrl: string;
|
||||
handleDelete: () => void;
|
||||
}) => {
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
const WebhookCard = ({ webhookUrl, onDelete }: WebhookCardProps) => {
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
return (
|
||||
<div className="flex justify-between w-full mb-3">
|
||||
{props.webhookUrl}
|
||||
{webhookUrl}
|
||||
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(props.webhookUrl);
|
||||
navigator.clipboard.writeText(webhookUrl);
|
||||
toast.success('Copied to clipboard');
|
||||
}}
|
||||
>
|
||||
@ -43,16 +44,13 @@ const WebhookCard = (props: {
|
||||
confirmButtonTitle="Yes, Confirm delete"
|
||||
handleConfirm={() => {
|
||||
setDeleteDialogOpen((preVal) => !preVal);
|
||||
props.handleDelete();
|
||||
onDelete();
|
||||
}}
|
||||
color="red"
|
||||
>
|
||||
<Typography variant="small">
|
||||
Are you sure you want to delete the variable{' '}
|
||||
<span className="bg-blue-100 p-0.5 rounded-sm">
|
||||
{props.webhookUrl}
|
||||
</span>
|
||||
?
|
||||
<span className="bg-blue-100 p-0.5 rounded-sm">{webhookUrl}</span>?
|
||||
</Typography>
|
||||
</ConfirmDialog>
|
||||
</div>
|
||||
|
@ -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, UpdateProdBranchResponse } from './types';
|
||||
import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain, updateProdBranch } 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 } from './types';
|
||||
import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain } from './mutations';
|
||||
|
||||
export interface GraphQLConfig {
|
||||
gqlEndpoint: string;
|
||||
@ -292,16 +292,4 @@ export class GQLClient {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async updateProdBranch (projectId: string, prodBranch: string): Promise<UpdateProdBranchResponse> {
|
||||
const { data } = await this.client.mutate({
|
||||
mutation: updateProdBranch,
|
||||
variables: {
|
||||
projectId,
|
||||
prodBranch
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -86,9 +86,3 @@ export const unauthenticateGitHub = gql`
|
||||
mutation {
|
||||
unauthenticateGitHub
|
||||
}`;
|
||||
|
||||
export const updateProdBranch = gql`
|
||||
mutation ($projectId: String!, $prodBranch: String!) {
|
||||
updateProdBranch(projectId: $projectId, prodBranch: $prodBranch)
|
||||
}
|
||||
`;
|
||||
|
@ -223,8 +223,10 @@ export type DeleteDomainResponse = {
|
||||
}
|
||||
|
||||
export type UpdateProjectInput = {
|
||||
name: string
|
||||
description: string
|
||||
name?: string
|
||||
description?: string
|
||||
prodBranch?: string
|
||||
webhooks?: string[]
|
||||
}
|
||||
|
||||
export type UpdateDomainInput = {
|
||||
@ -258,7 +260,3 @@ export type AuthenticateGitHubResponse = {
|
||||
export type UnauthenticateGitHubResponse = {
|
||||
unauthenticateGitHub: boolean
|
||||
}
|
||||
|
||||
export type UpdateProdBranchResponse = {
|
||||
updateProdBranch: boolean
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user