Add GQL mutation for redeploying deployment to production (#42)
* Add graphql mutation to redeploy deployment to production * Implement frontend to redeploy deployment to production --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
parent
5310d7c7d0
commit
0feeb9408d
@ -24,6 +24,7 @@
|
||||
{
|
||||
"allowArgumentsExplicitlyTypedAsAny": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": ["error", { "ignoreRestSiblings": true }]
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { User } from './entity/User';
|
||||
import { Organization } from './entity/Organization';
|
||||
import { UserOrganization } from './entity/UserOrganization';
|
||||
import { Project } from './entity/Project';
|
||||
import { Deployment } from './entity/Deployment';
|
||||
import { Deployment, Environment } from './entity/Deployment';
|
||||
import { ProjectMember } from './entity/ProjectMember';
|
||||
import { EnvironmentVariable } from './entity/EnvironmentVariable';
|
||||
|
||||
@ -243,4 +243,36 @@ export class Database {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async redeployToProdById (deploymentId: string): Promise<boolean> {
|
||||
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
||||
const deployment = await deploymentRepository.findOne({
|
||||
relations: {
|
||||
project: true,
|
||||
domain: true
|
||||
},
|
||||
where: {
|
||||
id: Number(deploymentId)
|
||||
}
|
||||
});
|
||||
|
||||
if (deployment === null) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
const { id, createdAt, updatedAt, ...updatedDeployment } = deployment;
|
||||
|
||||
if (updatedDeployment.environment === Environment.Production) {
|
||||
// TODO: Put isCurrent field in project
|
||||
updatedDeployment.isCurrent = true;
|
||||
}
|
||||
|
||||
await deploymentRepository.update({ id: Number(deploymentId) }, { domain: null, isCurrent: false });
|
||||
const savedUpdatedDeployment = await deploymentRepository.save(updatedDeployment);
|
||||
|
||||
if (savedUpdatedDeployment) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ export class Deployment {
|
||||
|
||||
@OneToOne(() => Domain)
|
||||
@JoinColumn({ name: 'domainId' })
|
||||
domain!: Domain;
|
||||
domain!: Domain | null;
|
||||
|
||||
@Column('varchar')
|
||||
branch!: string;
|
||||
|
@ -146,6 +146,15 @@ export const createResolvers = async (db: Database): Promise<any> => {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
redeployToProd: async (_: any, { deploymentId }: {deploymentId: string }) => {
|
||||
try {
|
||||
return db.redeployToProdById(deploymentId);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -127,6 +127,7 @@ type Mutation {
|
||||
addEnvironmentVariables(projectId: String!, environmentVariables: [AddEnvironmentVariableInput!]): Boolean!
|
||||
updateDeploymentToProd(deploymentId: String!): Boolean!
|
||||
updateProject(projectId: String!, updateProject: UpdateProjectInput): Boolean!
|
||||
redeployToProd(deploymentId: String!): Boolean!
|
||||
}
|
||||
|
||||
input AddEnvironmentVariableInput {
|
||||
|
12
packages/backend/test/fixtures/deployments.json
vendored
12
packages/backend/test/fixtures/deployments.json
vendored
@ -5,7 +5,7 @@
|
||||
"title": "nextjs-boilerplate-1",
|
||||
"status": "Building",
|
||||
"environment": "Production",
|
||||
"isCurrent": false,
|
||||
"isCurrent": true,
|
||||
"branch": "prod",
|
||||
"commitHash": "testXyz"
|
||||
},
|
||||
@ -15,7 +15,7 @@
|
||||
"title": "nextjs-boilerplate-2",
|
||||
"status": "Ready",
|
||||
"environment": "Preview",
|
||||
"isCurrent": true,
|
||||
"isCurrent": false,
|
||||
"branch": "prod",
|
||||
"commitHash": "testXyz"
|
||||
},
|
||||
@ -35,7 +35,7 @@
|
||||
"title": "nextjs-boilerplate-1",
|
||||
"status": "Building",
|
||||
"environment": "Production",
|
||||
"isCurrent": false,
|
||||
"isCurrent": true,
|
||||
"branch": "prod",
|
||||
"commitHash": "testXyz"
|
||||
},
|
||||
@ -45,7 +45,7 @@
|
||||
"title": "nextjs-boilerplate-2",
|
||||
"status": "Ready",
|
||||
"environment": "Preview",
|
||||
"isCurrent": true,
|
||||
"isCurrent": false,
|
||||
"branch": "prod",
|
||||
"commitHash": "testXyz"
|
||||
},
|
||||
@ -65,7 +65,7 @@
|
||||
"title": "nextjs-boilerplate-1",
|
||||
"status": "Building",
|
||||
"environment": "Production",
|
||||
"isCurrent": false,
|
||||
"isCurrent": true,
|
||||
"branch": "prod",
|
||||
"commitHash": "testXyz"
|
||||
},
|
||||
@ -75,7 +75,7 @@
|
||||
"title": "nextjs-boilerplate-2",
|
||||
"status": "Ready",
|
||||
"environment": "Preview",
|
||||
"isCurrent": true,
|
||||
"isCurrent": false,
|
||||
"branch": "prod",
|
||||
"commitHash": "testXyz"
|
||||
},
|
||||
|
@ -117,14 +117,12 @@ const main = async () => {
|
||||
await dataSource.initialize();
|
||||
|
||||
await generateTestData(dataSource);
|
||||
log('Data loaded successfully');
|
||||
} else {
|
||||
throw new Error('Database already exists');
|
||||
log('WARNING: Database already exists');
|
||||
}
|
||||
};
|
||||
|
||||
main().then(() => {
|
||||
log('Data loaded successfully');
|
||||
})
|
||||
.catch((err) => {
|
||||
log(err);
|
||||
});
|
||||
main().catch((err) => {
|
||||
log(err);
|
||||
});
|
||||
|
@ -50,6 +50,16 @@ const DeploymentDetailsCard = ({
|
||||
}
|
||||
};
|
||||
|
||||
const redeployToProd = async () => {
|
||||
const isRedeployed = await client.redeployToProd(deployment.id);
|
||||
if (isRedeployed) {
|
||||
await onUpdate();
|
||||
toast.success('Redeployed to production');
|
||||
} else {
|
||||
toast.error('Unable to redeploy to production');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2">
|
||||
<div className="col-span-2">
|
||||
@ -63,7 +73,9 @@ const DeploymentDetailsCard = ({
|
||||
/>
|
||||
</div>
|
||||
<Typography color="gray">
|
||||
{deployment.isProduction ? 'Production (Current)' : 'Preview'}
|
||||
{deployment.isProduction
|
||||
? `Production ${deployment.isCurrent ? '(Current)' : ''}`
|
||||
: 'Preview'}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
@ -94,6 +106,7 @@ const DeploymentDetailsCard = ({
|
||||
<hr className="my-3" />
|
||||
<MenuItem
|
||||
onClick={() => setRedeployToProduction(!redeployToProduction)}
|
||||
disabled={!(deployment.isProduction && deployment.isCurrent)}
|
||||
>
|
||||
^ Redeploy to production
|
||||
</MenuItem>
|
||||
@ -113,8 +126,8 @@ const DeploymentDetailsCard = ({
|
||||
open={changeToProduction}
|
||||
confirmButtonTitle="Change"
|
||||
color="blue"
|
||||
handleConfirm={() => {
|
||||
updateDeployment();
|
||||
handleConfirm={async () => {
|
||||
await updateDeployment();
|
||||
setChangeToProduction((preVal) => !preVal);
|
||||
}}
|
||||
>
|
||||
@ -140,7 +153,10 @@ const DeploymentDetailsCard = ({
|
||||
open={redeployToProduction}
|
||||
confirmButtonTitle="Redeploy"
|
||||
color="blue"
|
||||
handleConfirm={() => setRedeployToProduction((preVal) => !preVal)}
|
||||
handleConfirm={async () => {
|
||||
await redeployToProd();
|
||||
setRedeployToProduction((preVal) => !preVal);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Typography variant="small">
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
||||
|
||||
import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject } from './queries';
|
||||
import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, RemoveMemberResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput } from './types';
|
||||
import { removeMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation } from './mutations';
|
||||
import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, RemoveMemberResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse } from './types';
|
||||
import { removeMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd } from './mutations';
|
||||
|
||||
export interface GraphQLConfig {
|
||||
gqlEndpoint: string;
|
||||
@ -147,4 +147,15 @@ export class GQLClient {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async redeployToProd (deploymentId: string): Promise<RedeployToProdResponse> {
|
||||
const { data } = await this.client.mutate({
|
||||
mutation: redeployToProd,
|
||||
variables: {
|
||||
deploymentId
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -21,5 +21,10 @@ mutation ($deploymentId: String!) {
|
||||
export const updateProjectMutation = gql`
|
||||
mutation ($projectId: String!, $updateProject: UpdateProjectInput) {
|
||||
updateProject(projectId: $projectId, updateProject: $updateProject)
|
||||
}`;
|
||||
|
||||
export const redeployToProd = gql`
|
||||
mutation ($deploymentId: String!) {
|
||||
redeployToProd(deploymentId: $deploymentId)
|
||||
}
|
||||
`;
|
||||
|
@ -182,3 +182,7 @@ export type UpdateProjectInput = {
|
||||
name: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export type RedeployToProdResponse = {
|
||||
redeployToProd: boolean
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user