Fetch additional projects data and implement resolver for deployments query (#22)

* Add resolver function for fetching all deployments for a project

* Fetch project members while fetching organization data

* Map db project member and deployment entity to graphql type

* Fetch environment variables data while fetching organizations

* Add domain field in deployment

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
prathamesh0 2024-01-18 14:06:49 +05:30 committed by Ashwin Phatak
parent 9f1306f9cd
commit 62c969d1ff
6 changed files with 145 additions and 20 deletions

View File

@ -7,6 +7,9 @@ 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 { ProjectMember } from './entity/ProjectMember';
import { EnvironmentVariable } from './entity/EnvironmentVariable';
const log = debug('snowball:database'); const log = debug('snowball:database');
@ -37,16 +40,16 @@ export class Database {
return user; return user;
} }
async getOrganizationsbyUserId (userId: number) : Promise<Organization[]> { async getOrganizationsByUserId (userId: number) : Promise<Organization[]> {
const userOrganizationRepository = this.dataSource.getRepository(UserOrganization); const userOrganizationRepository = this.dataSource.getRepository(UserOrganization);
const userOrgs = await userOrganizationRepository.find({ const userOrgs = await userOrganizationRepository.find({
relations: { relations: {
user: true, member: true,
organization: true organization: true
}, },
where: { where: {
user: { member: {
id: userId id: userId
} }
} }
@ -56,7 +59,7 @@ export class Database {
return organizations; return organizations;
} }
async getProjectsbyOrganizationId (organizationId: number): Promise<Project[]> { async getProjectsByOrganizationId (organizationId: number): Promise<Project[]> {
const projectRepository = this.dataSource.getRepository(Project); const projectRepository = this.dataSource.getRepository(Project);
const projects = await projectRepository.find({ const projects = await projectRepository.find({
@ -73,4 +76,57 @@ export class Database {
return projects; return projects;
} }
async getDeploymentsByProjectId (projectId: string): Promise<Deployment[]> {
const deploymentRepository = this.dataSource.getRepository(Deployment);
const deployments = await deploymentRepository.find({
relations: {
project: true,
domain: true
},
where: {
project: {
id: projectId
}
}
});
return deployments;
}
async getProjectMembers (projectId: string): Promise<ProjectMember[]> {
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
const projectMembers = await projectMemberRepository.find({
relations: {
project: true,
member: true
},
where: {
project: {
id: projectId
}
}
});
return projectMembers;
}
async getEnvironmentVariablesByProjectId (projectId: string): Promise<EnvironmentVariable[]> {
const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable);
const environmentVariables = await environmentVariableRepository.find({
relations: {
project: true
},
where: {
project: {
id: projectId
}
}
});
return environmentVariables;
}
} }

View File

@ -10,6 +10,12 @@ import {
import { Project } from './Project'; import { Project } from './Project';
enum Environment {
Production = 'Production',
Preview = 'Preview',
Development = 'Development',
}
@Entity() @Entity()
export class EnvironmentVariable { export class EnvironmentVariable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
@ -22,7 +28,7 @@ export class EnvironmentVariable {
@Column({ @Column({
type: 'simple-array' type: 'simple-array'
}) })
environments!: string[]; environments!: Environment[];
@Column('varchar') @Column('varchar')
key!: string; key!: string;

View File

@ -12,9 +12,8 @@ import { Project } from './Project';
import { User } from './User'; import { User } from './User';
enum Permissions { enum Permissions {
Owner = 'Owner', View = 'View',
Maintainer = 'Maintainer', Edit = 'Edit'
Reader = 'Reader',
} }
@Entity() @Entity()
@ -24,16 +23,16 @@ export class ProjectMember {
@ManyToOne(() => User, { onDelete: 'CASCADE' }) @ManyToOne(() => User, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'userId' }) @JoinColumn({ name: 'userId' })
user!: User; member!: User;
@ManyToOne(() => Project, { onDelete: 'CASCADE' }) @ManyToOne(() => Project, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'projectId' }) @JoinColumn({ name: 'projectId' })
project!: Project; project!: Project;
@Column({ @Column({
enum: Permissions type: 'simple-array'
}) })
role!: Permissions; permissions!: Permissions[];
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Date;

View File

@ -24,7 +24,7 @@ export class UserOrganization {
@ManyToOne(() => User, { onDelete: 'CASCADE' }) @ManyToOne(() => User, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'userId' }) @JoinColumn({ name: 'userId' })
user!: User; member!: User;
@ManyToOne(() => Organization, { onDelete: 'CASCADE' }) @ManyToOne(() => Organization, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'organizationId' }) @JoinColumn({ name: 'organizationId' })

View File

@ -1,5 +1,5 @@
import { Database } from './database'; import { Database } from './database';
import { projectToGqlType } from './utils'; import { deploymentToGqlType, projectMemberToGqlType, projectToGqlType, environmentVariableToGqlType } from './utils';
export const createResolvers = async (db: Database): Promise<any> => { export const createResolvers = async (db: Database): Promise<any> => {
return { return {
@ -10,25 +10,47 @@ export const createResolvers = async (db: Database): Promise<any> => {
}, },
organizations: async (_:any, __: any, context: any) => { organizations: async (_:any, __: any, context: any) => {
const organizations = await db.getOrganizationsbyUserId(context.userId); const organizations = await db.getOrganizationsByUserId(context.userId);
const orgsWithProjectsPromises = organizations.map(async (org) => { const orgsWithProjectsPromises = organizations.map(async (org) => {
const dbProjects = await db.getProjectsbyOrganizationId(org.id); const dbProjects = await db.getProjectsByOrganizationId(org.id);
const projects = dbProjects.map(dbProject => { const projectsWithPromises = dbProjects.map(async (dbProject) => {
return projectToGqlType(dbProject); const dbProjectMembers = await db.getProjectMembers(dbProject.id);
const dbEnvironmentVariables = await db.getEnvironmentVariablesByProjectId(dbProject.id);
const projectMembers = dbProjectMembers.map(dbProjectMember => {
return projectMemberToGqlType(dbProjectMember);
}); });
const environmentVariables = dbEnvironmentVariables.map(dbEnvironmentVariable => {
return environmentVariableToGqlType(dbEnvironmentVariable);
});
return projectToGqlType(dbProject, projectMembers, environmentVariables);
});
const projects = await Promise.all(projectsWithPromises);
return { return {
...org, ...org,
projects projects
}; };
}); });
// TODO: Add organizationMembers field when / if required
const orgsWithProjects = await Promise.all(orgsWithProjectsPromises); const orgsWithProjects = await Promise.all(orgsWithProjectsPromises);
// TODO: Populate members field when / if required
return orgsWithProjects; return orgsWithProjects;
},
deployments: async (_: any, { projectId }: { projectId: string }) => {
const dbDeployments = await db.getDeploymentsByProjectId(projectId);
const deployments = dbDeployments.map(dbDeployment => {
return deploymentToGqlType(dbDeployment);
});
return deployments;
} }
} }
}; };

View File

@ -4,6 +4,9 @@ import toml from 'toml';
import debug from 'debug'; import debug from 'debug';
import { Project } from './entity/Project'; import { Project } from './entity/Project';
import { ProjectMember } from './entity/ProjectMember';
import { Deployment } from './entity/Deployment';
import { EnvironmentVariable } from './entity/EnvironmentVariable';
const log = debug('snowball:utils'); const log = debug('snowball:utils');
@ -22,7 +25,7 @@ export const getConfig = async <ConfigType>(
return config; return config;
}; };
export const projectToGqlType = (dbProject: Project): any => { export const projectToGqlType = (dbProject: Project, projectMembers: ProjectMember[], environmentVariables: EnvironmentVariable[]): any => {
return { return {
id: dbProject.id, id: dbProject.id,
owner: dbProject.owner, owner: dbProject.owner,
@ -33,7 +36,46 @@ export const projectToGqlType = (dbProject: Project): any => {
template: dbProject.template, template: dbProject.template,
framework: dbProject.framework, framework: dbProject.framework,
webhooks: dbProject.webhooks, webhooks: dbProject.webhooks,
members: projectMembers,
environmentVariables: environmentVariables,
createdAt: dbProject.createdAt, createdAt: dbProject.createdAt,
updatedAt: dbProject.updatedAt updatedAt: dbProject.updatedAt
}; };
}; };
// TODO: Add domain field to deployment
export const deploymentToGqlType = (dbDeployment: Deployment): any => {
return {
id: dbDeployment.id,
domain: dbDeployment.domain,
branch: dbDeployment.branch,
commitHash: dbDeployment.commitHash,
title: dbDeployment.title,
environment: dbDeployment.environment,
isCurrent: dbDeployment.isCurrent,
status: dbDeployment.status,
createdAt: dbDeployment.createdAt,
updatedAt: dbDeployment.updatedAt
};
};
export const projectMemberToGqlType = (dbProjectMember: ProjectMember): any => {
return {
id: dbProjectMember.id,
member: dbProjectMember.member,
permissions: dbProjectMember.permissions,
createdAt: dbProjectMember.createdAt,
updatedAt: dbProjectMember.updatedAt
};
};
export const environmentVariableToGqlType = (dbEnvironmentVariable: EnvironmentVariable): any => {
return {
id: dbEnvironmentVariable.id,
environments: dbEnvironmentVariable.environments,
key: dbEnvironmentVariable.key,
value: dbEnvironmentVariable.value,
createdAt: dbEnvironmentVariable.createdAt,
updatedAt: dbEnvironmentVariable.updatedAt
};
};