import { Injectable } from '@nestjs/common'; import { UserEnvironment } from './user-environments.model'; import { PrismaService } from '../prisma/prisma.service'; import { PubSubService } from '../pubsub/pubsub.service'; import * as E from 'fp-ts/Either'; import * as O from 'fp-ts/Option'; import { USER_ENVIRONMENT_ENV_DOES_NOT_EXISTS, USER_ENVIRONMENT_GLOBAL_ENV_DOES_NOT_EXISTS, USER_ENVIRONMENT_GLOBAL_ENV_DELETION_FAILED, USER_ENVIRONMENT_GLOBAL_ENV_EXISTS, USER_ENVIRONMENT_IS_NOT_GLOBAL, USER_ENVIRONMENT_UPDATE_FAILED, USER_ENVIRONMENT_INVALID_ENVIRONMENT_NAME, } from '../errors'; import { stringToJson } from '../utils'; @Injectable() export class UserEnvironmentsService { constructor( private readonly prisma: PrismaService, private readonly pubsub: PubSubService, ) {} /** * Fetch personal user environments * @param uid Users uid * @returns array of users personal environments */ async fetchUserEnvironments(uid: string) { const environments = await this.prisma.userEnvironment.findMany({ where: { userUid: uid, isGlobal: false, }, }); const userEnvironments: UserEnvironment[] = []; environments.forEach((environment) => { userEnvironments.push({ userUid: environment.userUid, id: environment.id, name: environment.name, variables: JSON.stringify(environment.variables), isGlobal: environment.isGlobal, }); }); return userEnvironments; } /** * Fetch users global environment * @param uid Users uid * @returns an `UserEnvironment` object */ async fetchUserGlobalEnvironment(uid: string) { const globalEnvironment = await this.prisma.userEnvironment.findFirst({ where: { userUid: uid, isGlobal: true, }, }); if (globalEnvironment != null) { return E.right({ userUid: globalEnvironment.userUid, id: globalEnvironment.id, name: globalEnvironment.name, variables: JSON.stringify(globalEnvironment.variables), isGlobal: globalEnvironment.isGlobal, }); } return E.left(USER_ENVIRONMENT_ENV_DOES_NOT_EXISTS); } /** * Create a personal or global user environment * @param uid Users uid * @param name environments name, null if the environment is global * @param variables environment variables * @param isGlobal flag to indicate type of environment to create * @returns an `UserEnvironment` object */ async createUserEnvironment( uid: string, name: string, variables: string, isGlobal: boolean, ) { // Check for existing global env for a user if exists error out to avoid recreation if (isGlobal) { const globalEnvExists = await this.checkForExistingGlobalEnv(uid); if (!O.isNone(globalEnvExists)) return E.left(USER_ENVIRONMENT_GLOBAL_ENV_EXISTS); } if (name === null && !isGlobal) return E.left(USER_ENVIRONMENT_INVALID_ENVIRONMENT_NAME); const envVariables = stringToJson(variables); if (E.isLeft(envVariables)) return E.left(envVariables.left); const createdEnvironment = await this.prisma.userEnvironment.create({ data: { userUid: uid, name: name, variables: envVariables.right, isGlobal: isGlobal, }, }); const userEnvironment: UserEnvironment = { userUid: createdEnvironment.userUid, id: createdEnvironment.id, name: createdEnvironment.name, variables: JSON.stringify(createdEnvironment.variables), isGlobal: createdEnvironment.isGlobal, }; // Publish subscription for environment creation await this.pubsub.publish( `user_environment/${userEnvironment.userUid}/created`, userEnvironment, ); return E.right(userEnvironment); } /** * Update an existing personal or global user environment * @param id environment id * @param name environments name * @param variables environment variables * @returns an Either of `UserEnvironment` or error */ async updateUserEnvironment(id: string, name: string, variables: string) { const envVariables = stringToJson(variables); if (E.isLeft(envVariables)) return E.left(envVariables.left); try { const updatedEnvironment = await this.prisma.userEnvironment.update({ where: { id: id }, data: { name: name, variables: envVariables.right, }, }); const updatedUserEnvironment: UserEnvironment = { userUid: updatedEnvironment.userUid, id: updatedEnvironment.id, name: updatedEnvironment.name, variables: JSON.stringify(updatedEnvironment.variables), isGlobal: updatedEnvironment.isGlobal, }; // Publish subscription for environment update await this.pubsub.publish( `user_environment/${updatedUserEnvironment.userUid}/updated`, updatedUserEnvironment, ); return E.right(updatedUserEnvironment); } catch (e) { return E.left(USER_ENVIRONMENT_ENV_DOES_NOT_EXISTS); } } /** * Delete an existing personal user environment based on environment id * @param uid users uid * @param id environment id * @returns an Either of deleted `UserEnvironment` or error */ async deleteUserEnvironment(uid: string, id: string) { try { // check if id is of a global environment if it is, don't delete and error out const globalEnvExists = await this.checkForExistingGlobalEnv(uid); if (O.isSome(globalEnvExists)) { const globalEnv = globalEnvExists.value; if (globalEnv.id === id) { return E.left(USER_ENVIRONMENT_GLOBAL_ENV_DELETION_FAILED); } } const deletedEnvironment = await this.prisma.userEnvironment.delete({ where: { id: id, }, }); const deletedUserEnvironment: UserEnvironment = { userUid: deletedEnvironment.userUid, id: deletedEnvironment.id, name: deletedEnvironment.name, variables: JSON.stringify(deletedEnvironment.variables), isGlobal: deletedEnvironment.isGlobal, }; // Publish subscription for environment deletion await this.pubsub.publish( `user_environment/${deletedUserEnvironment.userUid}/deleted`, deletedUserEnvironment, ); return E.right(true); } catch (e) { return E.left(USER_ENVIRONMENT_ENV_DOES_NOT_EXISTS); } } /** * Deletes all existing personal user environments * @param uid user uid * @returns a count of environments deleted */ async deleteUserEnvironments(uid: string) { const deletedEnvironments = await this.prisma.userEnvironment.deleteMany({ where: { userUid: uid, isGlobal: false, }, }); // Publish subscription for multiple environment deletions await this.pubsub.publish( `user_environment/${uid}/deleted_many`, deletedEnvironments.count, ); return deletedEnvironments.count; } /** * Removes all existing variables in a users global environment * @param uid users uid * @param id environment id * @returns an `` of environments deleted */ async clearGlobalEnvironments(uid: string, id: string) { const globalEnvExists = await this.checkForExistingGlobalEnv(uid); if (O.isNone(globalEnvExists)) return E.left(USER_ENVIRONMENT_GLOBAL_ENV_DOES_NOT_EXISTS); const env = globalEnvExists.value; if (env.id === id) { try { const updatedEnvironment = await this.prisma.userEnvironment.update({ where: { id: id }, data: { variables: [], }, }); const updatedUserEnvironment: UserEnvironment = { userUid: updatedEnvironment.userUid, id: updatedEnvironment.id, name: updatedEnvironment.name, variables: JSON.stringify(updatedEnvironment.variables), isGlobal: updatedEnvironment.isGlobal, }; // Publish subscription for environment update await this.pubsub.publish( `user_environment/${updatedUserEnvironment.userUid}/updated`, updatedUserEnvironment, ); return E.right(updatedUserEnvironment); } catch (e) { return E.left(USER_ENVIRONMENT_UPDATE_FAILED); } } else return E.left(USER_ENVIRONMENT_IS_NOT_GLOBAL); } // Method to check for existing global environments for a given user uid private async checkForExistingGlobalEnv(uid: string) { const globalEnv = await this.prisma.userEnvironment.findFirst({ where: { userUid: uid, isGlobal: true, }, }); if (globalEnv == null) return O.none; return O.some(globalEnv); } }