forked from cerc-io/snowballtools-base
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
|
"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 { Organization } from './entity/Organization';
|
||||||
import { UserOrganization } from './entity/UserOrganization';
|
import { UserOrganization } from './entity/UserOrganization';
|
||||||
import { Project } from './entity/Project';
|
import { Project } from './entity/Project';
|
||||||
import { Deployment } from './entity/Deployment';
|
import { Deployment, Environment } from './entity/Deployment';
|
||||||
import { ProjectMember } from './entity/ProjectMember';
|
import { ProjectMember } from './entity/ProjectMember';
|
||||||
import { EnvironmentVariable } from './entity/EnvironmentVariable';
|
import { EnvironmentVariable } from './entity/EnvironmentVariable';
|
||||||
|
|
||||||
@ -243,4 +243,36 @@ export class Database {
|
|||||||
return false;
|
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)
|
@OneToOne(() => Domain)
|
||||||
@JoinColumn({ name: 'domainId' })
|
@JoinColumn({ name: 'domainId' })
|
||||||
domain!: Domain;
|
domain!: Domain | null;
|
||||||
|
|
||||||
@Column('varchar')
|
@Column('varchar')
|
||||||
branch!: string;
|
branch!: string;
|
||||||
|
@ -146,6 +146,15 @@ export const createResolvers = async (db: Database): Promise<any> => {
|
|||||||
log(err);
|
log(err);
|
||||||
return false;
|
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!
|
addEnvironmentVariables(projectId: String!, environmentVariables: [AddEnvironmentVariableInput!]): Boolean!
|
||||||
updateDeploymentToProd(deploymentId: String!): Boolean!
|
updateDeploymentToProd(deploymentId: String!): Boolean!
|
||||||
updateProject(projectId: String!, updateProject: UpdateProjectInput): Boolean!
|
updateProject(projectId: String!, updateProject: UpdateProjectInput): Boolean!
|
||||||
|
redeployToProd(deploymentId: String!): Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
input AddEnvironmentVariableInput {
|
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",
|
"title": "nextjs-boilerplate-1",
|
||||||
"status": "Building",
|
"status": "Building",
|
||||||
"environment": "Production",
|
"environment": "Production",
|
||||||
"isCurrent": false,
|
"isCurrent": true,
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commitHash": "testXyz"
|
"commitHash": "testXyz"
|
||||||
},
|
},
|
||||||
@ -15,7 +15,7 @@
|
|||||||
"title": "nextjs-boilerplate-2",
|
"title": "nextjs-boilerplate-2",
|
||||||
"status": "Ready",
|
"status": "Ready",
|
||||||
"environment": "Preview",
|
"environment": "Preview",
|
||||||
"isCurrent": true,
|
"isCurrent": false,
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commitHash": "testXyz"
|
"commitHash": "testXyz"
|
||||||
},
|
},
|
||||||
@ -35,7 +35,7 @@
|
|||||||
"title": "nextjs-boilerplate-1",
|
"title": "nextjs-boilerplate-1",
|
||||||
"status": "Building",
|
"status": "Building",
|
||||||
"environment": "Production",
|
"environment": "Production",
|
||||||
"isCurrent": false,
|
"isCurrent": true,
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commitHash": "testXyz"
|
"commitHash": "testXyz"
|
||||||
},
|
},
|
||||||
@ -45,7 +45,7 @@
|
|||||||
"title": "nextjs-boilerplate-2",
|
"title": "nextjs-boilerplate-2",
|
||||||
"status": "Ready",
|
"status": "Ready",
|
||||||
"environment": "Preview",
|
"environment": "Preview",
|
||||||
"isCurrent": true,
|
"isCurrent": false,
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commitHash": "testXyz"
|
"commitHash": "testXyz"
|
||||||
},
|
},
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"title": "nextjs-boilerplate-1",
|
"title": "nextjs-boilerplate-1",
|
||||||
"status": "Building",
|
"status": "Building",
|
||||||
"environment": "Production",
|
"environment": "Production",
|
||||||
"isCurrent": false,
|
"isCurrent": true,
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commitHash": "testXyz"
|
"commitHash": "testXyz"
|
||||||
},
|
},
|
||||||
@ -75,7 +75,7 @@
|
|||||||
"title": "nextjs-boilerplate-2",
|
"title": "nextjs-boilerplate-2",
|
||||||
"status": "Ready",
|
"status": "Ready",
|
||||||
"environment": "Preview",
|
"environment": "Preview",
|
||||||
"isCurrent": true,
|
"isCurrent": false,
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commitHash": "testXyz"
|
"commitHash": "testXyz"
|
||||||
},
|
},
|
||||||
|
@ -117,14 +117,12 @@ const main = async () => {
|
|||||||
await dataSource.initialize();
|
await dataSource.initialize();
|
||||||
|
|
||||||
await generateTestData(dataSource);
|
await generateTestData(dataSource);
|
||||||
|
log('Data loaded successfully');
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Database already exists');
|
log('WARNING: Database already exists');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
main().then(() => {
|
main().catch((err) => {
|
||||||
log('Data loaded successfully');
|
log(err);
|
||||||
})
|
});
|
||||||
.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 (
|
return (
|
||||||
<div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2">
|
<div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2">
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
@ -63,7 +73,9 @@ const DeploymentDetailsCard = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Typography color="gray">
|
<Typography color="gray">
|
||||||
{deployment.isProduction ? 'Production (Current)' : 'Preview'}
|
{deployment.isProduction
|
||||||
|
? `Production ${deployment.isCurrent ? '(Current)' : ''}`
|
||||||
|
: 'Preview'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
@ -94,6 +106,7 @@ const DeploymentDetailsCard = ({
|
|||||||
<hr className="my-3" />
|
<hr className="my-3" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => setRedeployToProduction(!redeployToProduction)}
|
onClick={() => setRedeployToProduction(!redeployToProduction)}
|
||||||
|
disabled={!(deployment.isProduction && deployment.isCurrent)}
|
||||||
>
|
>
|
||||||
^ Redeploy to production
|
^ Redeploy to production
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -113,8 +126,8 @@ const DeploymentDetailsCard = ({
|
|||||||
open={changeToProduction}
|
open={changeToProduction}
|
||||||
confirmButtonTitle="Change"
|
confirmButtonTitle="Change"
|
||||||
color="blue"
|
color="blue"
|
||||||
handleConfirm={() => {
|
handleConfirm={async () => {
|
||||||
updateDeployment();
|
await updateDeployment();
|
||||||
setChangeToProduction((preVal) => !preVal);
|
setChangeToProduction((preVal) => !preVal);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -140,7 +153,10 @@ const DeploymentDetailsCard = ({
|
|||||||
open={redeployToProduction}
|
open={redeployToProduction}
|
||||||
confirmButtonTitle="Redeploy"
|
confirmButtonTitle="Redeploy"
|
||||||
color="blue"
|
color="blue"
|
||||||
handleConfirm={() => setRedeployToProduction((preVal) => !preVal)}
|
handleConfirm={async () => {
|
||||||
|
await redeployToProd();
|
||||||
|
setRedeployToProduction((preVal) => !preVal);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Typography variant="small">
|
<Typography variant="small">
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
||||||
|
|
||||||
import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject } from './queries';
|
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 { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, RemoveMemberResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse } from './types';
|
||||||
import { removeMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation } from './mutations';
|
import { removeMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd } from './mutations';
|
||||||
|
|
||||||
export interface GraphQLConfig {
|
export interface GraphQLConfig {
|
||||||
gqlEndpoint: string;
|
gqlEndpoint: string;
|
||||||
@ -147,4 +147,15 @@ export class GQLClient {
|
|||||||
|
|
||||||
return data;
|
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`
|
export const updateProjectMutation = gql`
|
||||||
mutation ($projectId: String!, $updateProject: UpdateProjectInput) {
|
mutation ($projectId: String!, $updateProject: UpdateProjectInput) {
|
||||||
updateProject(projectId: $projectId, updateProject: $updateProject)
|
updateProject(projectId: $projectId, updateProject: $updateProject)
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const redeployToProd = gql`
|
||||||
|
mutation ($deploymentId: String!) {
|
||||||
|
redeployToProd(deploymentId: $deploymentId)
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -182,3 +182,7 @@ export type UpdateProjectInput = {
|
|||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RedeployToProdResponse = {
|
||||||
|
redeployToProd: boolean
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user