forked from cerc-io/snowballtools-base
Refactor mutation GQL methods to service class and display organization name (#53)
* Refactor mutation methods to service class * Refactor database methods to service class * Display organization name in sidebar * Handle review comments --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
@@ -1,24 +1,20 @@
|
||||
import { DataSource, DeepPartial } from 'typeorm';
|
||||
import { DataSource, DeepPartial, FindManyOptions, FindOneOptions } from 'typeorm';
|
||||
import path from 'path';
|
||||
import debug from 'debug';
|
||||
import assert from 'assert';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { lowercase, numbers } from 'nanoid-dictionary';
|
||||
|
||||
import { DatabaseConfig } from './config';
|
||||
import { User } from './entity/User';
|
||||
import { Organization } from './entity/Organization';
|
||||
import { Project } from './entity/Project';
|
||||
import { Deployment, Environment } from './entity/Deployment';
|
||||
import { Permission, ProjectMember } from './entity/ProjectMember';
|
||||
import { Deployment } from './entity/Deployment';
|
||||
import { ProjectMember } from './entity/ProjectMember';
|
||||
import { EnvironmentVariable } from './entity/EnvironmentVariable';
|
||||
import { Domain } from './entity/Domain';
|
||||
import { PROJECT_DOMAIN } from './constants';
|
||||
|
||||
const log = debug('snowball:database');
|
||||
|
||||
const nanoid = customAlphabet(lowercase + numbers, 8);
|
||||
|
||||
// TODO: Fix order of methods
|
||||
export class Database {
|
||||
private dataSource: DataSource;
|
||||
@@ -38,11 +34,16 @@ export class Database {
|
||||
log('database initialized');
|
||||
}
|
||||
|
||||
async getUser (userId: number): Promise<User | null> {
|
||||
async getUser (options: FindOneOptions<User>): Promise<User | null> {
|
||||
const userRepository = this.dataSource.getRepository(User);
|
||||
const user = await userRepository.findOneBy({
|
||||
id: userId
|
||||
});
|
||||
const user = await userRepository.findOne(options);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async createUser (data: DeepPartial<User>): Promise<User> {
|
||||
const userRepository = this.dataSource.getRepository(User);
|
||||
const user = await userRepository.save(data);
|
||||
|
||||
return user;
|
||||
}
|
||||
@@ -146,6 +147,27 @@ export class Database {
|
||||
return deployments;
|
||||
}
|
||||
|
||||
async getDeployment (options: FindOneOptions<Deployment>): Promise<Deployment | null> {
|
||||
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
||||
const deployment = await deploymentRepository.findOne(options);
|
||||
|
||||
return deployment;
|
||||
}
|
||||
|
||||
async getDomains (options: FindManyOptions<Domain>): Promise<Domain[]> {
|
||||
const domainRepository = this.dataSource.getRepository(Domain);
|
||||
const domains = await domainRepository.find(options);
|
||||
|
||||
return domains;
|
||||
}
|
||||
|
||||
async createDeployement (data: DeepPartial<Deployment>): Promise<Deployment> {
|
||||
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
||||
const deployment = await deploymentRepository.save(data);
|
||||
|
||||
return deployment;
|
||||
}
|
||||
|
||||
async getProjectMembersByProjectId (projectId: string): Promise<ProjectMember[]> {
|
||||
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
|
||||
|
||||
@@ -201,60 +223,18 @@ export class Database {
|
||||
}
|
||||
}
|
||||
|
||||
async addProjectMember (projectId: string, data: {
|
||||
email: string,
|
||||
permissions: Permission[]
|
||||
}): Promise<boolean> {
|
||||
async addProjectMember (data: DeepPartial<ProjectMember>): Promise<ProjectMember> {
|
||||
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
|
||||
const userRepository = this.dataSource.getRepository(User);
|
||||
const newProjectMember = await projectMemberRepository.save(data);
|
||||
|
||||
let user = await userRepository.findOneBy({
|
||||
email: data.email
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
user = await userRepository.save({
|
||||
email: data.email,
|
||||
isVerified: false
|
||||
});
|
||||
}
|
||||
|
||||
const newProjectMember = await projectMemberRepository.save({
|
||||
project: {
|
||||
id: projectId
|
||||
},
|
||||
permissions: data.permissions,
|
||||
isPending: true,
|
||||
member: {
|
||||
id: user.id
|
||||
}
|
||||
});
|
||||
|
||||
return Boolean(newProjectMember);
|
||||
return newProjectMember;
|
||||
}
|
||||
|
||||
async addEnvironmentVariablesByProjectId (projectId: string, environmentVariables: {
|
||||
environments: string[];
|
||||
key: string;
|
||||
value: string;
|
||||
}[]): Promise<boolean> {
|
||||
async addEnvironmentVariables (data: DeepPartial<EnvironmentVariable>[]): Promise<EnvironmentVariable[]> {
|
||||
const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable);
|
||||
const savedEnvironmentVariables = await environmentVariableRepository.save(data);
|
||||
|
||||
const formattedEnvironmentVariables = environmentVariables.map((environmentVariable) => {
|
||||
return environmentVariable.environments.map((environment) => {
|
||||
return ({
|
||||
key: environmentVariable.key,
|
||||
value: environmentVariable.value,
|
||||
environment: environment as Environment,
|
||||
project: Object.assign(new Project(), {
|
||||
id: projectId
|
||||
})
|
||||
});
|
||||
});
|
||||
}).flat();
|
||||
|
||||
const savedEnvironmentVariables = await environmentVariableRepository.save(formattedEnvironmentVariables);
|
||||
return savedEnvironmentVariables.length > 0;
|
||||
return savedEnvironmentVariables;
|
||||
}
|
||||
|
||||
async updateEnvironmentVariable (environmentVariableId: string, update: DeepPartial<EnvironmentVariable>): Promise<boolean> {
|
||||
@@ -363,40 +343,6 @@ export class Database {
|
||||
}
|
||||
}
|
||||
|
||||
async redeployToProdById (userId: string, deploymentId: string): Promise<Deployment> {
|
||||
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
||||
const deployment = await deploymentRepository.findOne({
|
||||
relations: {
|
||||
project: true,
|
||||
domain: true,
|
||||
createdBy: true
|
||||
},
|
||||
where: {
|
||||
id: deploymentId
|
||||
}
|
||||
});
|
||||
|
||||
if (deployment === null) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
const { createdAt, updatedAt, ...updatedDeployment } = deployment;
|
||||
|
||||
if (updatedDeployment.environment === Environment.Production) {
|
||||
// TODO: Put isCurrent field in project
|
||||
updatedDeployment.isCurrent = true;
|
||||
updatedDeployment.createdBy = Object.assign(new User(), {
|
||||
id: Number(userId)
|
||||
});
|
||||
}
|
||||
|
||||
await deploymentRepository.update({ id: deploymentId }, { domain: null, isCurrent: false });
|
||||
|
||||
updatedDeployment.id = nanoid();
|
||||
updatedDeployment.url = `${updatedDeployment.id}-${updatedDeployment.project.subDomain}`;
|
||||
|
||||
return deploymentRepository.save(updatedDeployment);
|
||||
}
|
||||
|
||||
async deleteProjectById (projectId: string): Promise<boolean> {
|
||||
const projectRepository = this.dataSource.getRepository(Project);
|
||||
const project = await projectRepository.findOneOrFail({
|
||||
@@ -416,16 +362,6 @@ export class Database {
|
||||
async deleteDomainById (domainId: string): Promise<boolean> {
|
||||
const domainRepository = this.dataSource.getRepository(Domain);
|
||||
|
||||
const domainsRedirectedFrom = await domainRepository.find({
|
||||
where: {
|
||||
redirectToId: Number(domainId)
|
||||
}
|
||||
});
|
||||
|
||||
if (domainsRedirectedFrom.length > 0) {
|
||||
throw new Error('Cannot delete domain since it has redirects from other domains');
|
||||
}
|
||||
|
||||
const deleteResult = await domainRepository.softDelete({ id: Number(domainId) });
|
||||
|
||||
if (deleteResult.affected) {
|
||||
|
||||
@@ -7,10 +7,10 @@ import { OAuthApp } from '@octokit/oauth-app';
|
||||
import { Service } from './service';
|
||||
import { Database } from './database';
|
||||
import { isUserOwner } from './utils';
|
||||
import { Environment } from './entity/Deployment';
|
||||
import { Permission } from './entity/ProjectMember';
|
||||
import { Domain } from './entity/Domain';
|
||||
import { Project } from './entity/Project';
|
||||
import { EnvironmentVariable } from './entity/EnvironmentVariable';
|
||||
|
||||
const log = debug('snowball:database');
|
||||
|
||||
@@ -86,12 +86,7 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv
|
||||
permissions: Permission[]
|
||||
}
|
||||
}) => {
|
||||
try {
|
||||
return await db.updateProjectMemberById(projectMemberId, data);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
return service.updateProjectMember(projectMemberId, data);
|
||||
},
|
||||
|
||||
addProjectMember: async (_: any, { projectId, data }: {
|
||||
@@ -101,100 +96,43 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv
|
||||
permissions: Permission[]
|
||||
}
|
||||
}) => {
|
||||
try {
|
||||
// TODO: Send invitation
|
||||
return await db.addProjectMember(projectId, data);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
return service.addProjectMember(projectId, data);
|
||||
},
|
||||
|
||||
addEnvironmentVariables: async (_: any, { projectId, environmentVariables }: { projectId: string, environmentVariables: { environments: string[], key: string, value: string}[] }) => {
|
||||
try {
|
||||
return await db.addEnvironmentVariablesByProjectId(projectId, environmentVariables);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
addEnvironmentVariables: async (_: any, { projectId, data }: { projectId: string, data: { environments: string[], key: string, value: string}[] }) => {
|
||||
return service.addEnvironmentVariables(projectId, data);
|
||||
},
|
||||
|
||||
updateEnvironmentVariable: async (_: any, { environmentVariableId, environmentVariable }: { environmentVariableId: string, environmentVariable : {
|
||||
key: string
|
||||
value: string
|
||||
}}) => {
|
||||
try {
|
||||
return await db.updateEnvironmentVariable(environmentVariableId, environmentVariable);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
updateEnvironmentVariable: async (_: any, { environmentVariableId, data }: { environmentVariableId: string, data : DeepPartial<EnvironmentVariable>}) => {
|
||||
return service.updateEnvironmentVariable(environmentVariableId, data);
|
||||
},
|
||||
|
||||
removeEnvironmentVariable: async (_: any, { environmentVariableId }: { environmentVariableId: string}) => {
|
||||
try {
|
||||
return await db.deleteEnvironmentVariable(environmentVariableId);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
return service.removeEnvironmentVariable(environmentVariableId);
|
||||
},
|
||||
|
||||
updateDeploymentToProd: async (_: any, { deploymentId }: { deploymentId: string }) => {
|
||||
try {
|
||||
return await db.updateDeploymentById(deploymentId, {
|
||||
environment: Environment.Production
|
||||
});
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
return service.updateDeploymentToProd(deploymentId);
|
||||
},
|
||||
|
||||
addProject: async (_: any, { projectDetails }: { projectDetails: DeepPartial<Project> }, context: any) => {
|
||||
try {
|
||||
await db.addProject(context.userId, projectDetails);
|
||||
return true;
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
addProject: async (_: any, { data }: { data: DeepPartial<Project> }, context: any) => {
|
||||
return service.addProject(context.userId, data);
|
||||
},
|
||||
|
||||
updateProject: async (_: any, { projectId, projectDetails }: { projectId: string, projectDetails: DeepPartial<Project> }) => {
|
||||
try {
|
||||
return await db.updateProjectById(projectId, projectDetails);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
return service.updateProject(projectId, projectDetails);
|
||||
},
|
||||
|
||||
redeployToProd: async (_: any, { deploymentId }: { deploymentId: string }, context: any) => {
|
||||
try {
|
||||
return await db.redeployToProdById(context.userId, deploymentId);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
return service.redeployToProd(context.userId, deploymentId);
|
||||
},
|
||||
|
||||
deleteProject: async (_: any, { projectId }: { projectId: string }) => {
|
||||
try {
|
||||
return await db.deleteProjectById(projectId);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
return service.deleteProject(projectId);
|
||||
},
|
||||
|
||||
deleteDomain: async (_: any, { domainId }: { domainId: string }) => {
|
||||
try {
|
||||
return await db.deleteDomainById(domainId);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
return service.deleteDomain(domainId);
|
||||
},
|
||||
|
||||
rollbackDeployment: async (_: any, { projectId, deploymentId }: {deploymentId: string, projectId: string }) => {
|
||||
|
||||
@@ -184,8 +184,8 @@ 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!
|
||||
addEnvironmentVariables(projectId: String!, data: [AddEnvironmentVariableInput!]): Boolean!
|
||||
updateEnvironmentVariable(environmentVariableId: String!, data: UpdateEnvironmentVariableInput!): Boolean!
|
||||
removeEnvironmentVariable(environmentVariableId: String!): Boolean!
|
||||
updateDeploymentToProd(deploymentId: String!): Boolean!
|
||||
addProject(projectDetails: AddProjectInput): Boolean!
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import debug from 'debug';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { lowercase, numbers } from 'nanoid-dictionary';
|
||||
|
||||
import { Database } from './database';
|
||||
import { Deployment } from './entity/Deployment';
|
||||
import { Deployment, Environment } from './entity/Deployment';
|
||||
import { Domain } from './entity/Domain';
|
||||
import { EnvironmentVariable } from './entity/EnvironmentVariable';
|
||||
import { Organization } from './entity/Organization';
|
||||
import { Project } from './entity/Project';
|
||||
import { ProjectMember } from './entity/ProjectMember';
|
||||
import { Permission, ProjectMember } from './entity/ProjectMember';
|
||||
import { User } from './entity/User';
|
||||
import { DeepPartial } from 'typeorm';
|
||||
|
||||
const log = debug('snowball:service');
|
||||
|
||||
const nanoid = customAlphabet(lowercase + numbers, 8);
|
||||
|
||||
export class Service {
|
||||
private db: Database;
|
||||
@@ -15,7 +24,11 @@ export class Service {
|
||||
}
|
||||
|
||||
async getUser (userId: number): Promise<User | null> {
|
||||
return this.db.getUser(userId);
|
||||
return this.db.getUser({
|
||||
where: {
|
||||
id: userId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getOrganizationsByUserId (userId: number): Promise<Organization[]> {
|
||||
@@ -57,4 +70,189 @@ export class Service {
|
||||
const dbDomains = await this.db.getDomainsByProjectId(projectId);
|
||||
return dbDomains;
|
||||
}
|
||||
|
||||
async updateProjectMember (projectMemberId: string, data: {permissions: Permission[]}): Promise<boolean> {
|
||||
try {
|
||||
return await this.db.updateProjectMemberById(projectMemberId, data);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async addProjectMember (projectId: string,
|
||||
data: {
|
||||
email: string,
|
||||
permissions: Permission[]
|
||||
}): Promise<boolean> {
|
||||
try {
|
||||
// TODO: Send invitation
|
||||
let user = await this.db.getUser({
|
||||
where: {
|
||||
email: data.email
|
||||
}
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
user = await this.db.createUser({
|
||||
email: data.email
|
||||
});
|
||||
}
|
||||
|
||||
const newProjectMember = await this.db.addProjectMember({
|
||||
project: {
|
||||
id: projectId
|
||||
},
|
||||
permissions: data.permissions,
|
||||
isPending: true,
|
||||
member: {
|
||||
id: user.id
|
||||
}
|
||||
});
|
||||
|
||||
return Boolean(newProjectMember);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async addEnvironmentVariables (projectId: string, data: { environments: string[], key: string, value: string}[]): Promise<boolean> {
|
||||
try {
|
||||
const formattedEnvironmentVariables = data.map((environmentVariable) => {
|
||||
return environmentVariable.environments.map((environment) => {
|
||||
return ({
|
||||
key: environmentVariable.key,
|
||||
value: environmentVariable.value,
|
||||
environment: environment as Environment,
|
||||
project: Object.assign(new Project(), {
|
||||
id: projectId
|
||||
})
|
||||
});
|
||||
});
|
||||
}).flat();
|
||||
|
||||
const savedEnvironmentVariables = await this.db.addEnvironmentVariables(formattedEnvironmentVariables);
|
||||
return savedEnvironmentVariables.length > 0;
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async updateEnvironmentVariable (environmentVariableId: string, data : DeepPartial<EnvironmentVariable>): Promise<boolean> {
|
||||
try {
|
||||
return await this.db.updateEnvironmentVariable(environmentVariableId, data);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async removeEnvironmentVariable (environmentVariableId: string): Promise<boolean> {
|
||||
try {
|
||||
return await this.db.deleteEnvironmentVariable(environmentVariableId);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async updateDeploymentToProd (deploymentId: string): Promise<boolean> {
|
||||
try {
|
||||
return await this.db.updateDeploymentById(deploymentId, {
|
||||
environment: Environment.Production
|
||||
});
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async addProject (userId: string, data: DeepPartial<Project>): Promise<boolean> {
|
||||
try {
|
||||
await this.db.addProject(userId, data);
|
||||
return true;
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async updateProject (projectId: string, data: DeepPartial<Project>): Promise<boolean> {
|
||||
try {
|
||||
return await this.db.updateProjectById(projectId, data);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteProject (projectId: string): Promise<boolean> {
|
||||
try {
|
||||
return await this.db.deleteProjectById(projectId);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteDomain (domainId: string): Promise<boolean> {
|
||||
try {
|
||||
const domainsRedirectedFrom = await this.db.getDomains({
|
||||
where: {
|
||||
redirectToId: Number(domainId)
|
||||
}
|
||||
});
|
||||
|
||||
if (domainsRedirectedFrom.length > 0) {
|
||||
throw new Error('Cannot delete domain since it has redirects from other domains');
|
||||
}
|
||||
|
||||
return await this.db.deleteDomainById(domainId);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async redeployToProd (userId: string, deploymentId: string): Promise<Deployment| boolean> {
|
||||
try {
|
||||
const deployment = await this.db.getDeployment({
|
||||
relations: {
|
||||
project: true,
|
||||
domain: true,
|
||||
createdBy: true
|
||||
},
|
||||
where: {
|
||||
id: deploymentId
|
||||
}
|
||||
});
|
||||
|
||||
if (deployment === null) {
|
||||
throw new Error('Deployment not found');
|
||||
}
|
||||
|
||||
const { createdAt, updatedAt, ...updatedDeployment } = deployment;
|
||||
|
||||
if (updatedDeployment.environment === Environment.Production) {
|
||||
// TODO: Put isCurrent field in project
|
||||
updatedDeployment.isCurrent = true;
|
||||
updatedDeployment.createdBy = Object.assign(new User(), {
|
||||
id: Number(userId)
|
||||
});
|
||||
}
|
||||
|
||||
updatedDeployment.id = nanoid();
|
||||
updatedDeployment.url = `${updatedDeployment.id}-${updatedDeployment.project.subDomain}`;
|
||||
|
||||
const oldDeployment = await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false });
|
||||
const newDeployement = await this.db.createDeployement(updatedDeployment);
|
||||
|
||||
return oldDeployment && Boolean(newDeployement);
|
||||
} catch (err) {
|
||||
log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user