feat: teamEnvironment module added
This commit is contained in:
@@ -9,6 +9,7 @@ import { UserEnvironmentsModule } from './user-environment/user-environments.mod
|
|||||||
import { UserHistoryModule } from './user-history/user-history.module';
|
import { UserHistoryModule } from './user-history/user-history.module';
|
||||||
import { subscriptionContextCookieParser } from './auth/helper';
|
import { subscriptionContextCookieParser } from './auth/helper';
|
||||||
import { TeamModule } from './team/team.module';
|
import { TeamModule } from './team/team.module';
|
||||||
|
import { TeamEnvironmentsModule } from './team-environments/team-environments.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -47,6 +48,7 @@ import { TeamModule } from './team/team.module';
|
|||||||
UserEnvironmentsModule,
|
UserEnvironmentsModule,
|
||||||
UserHistoryModule,
|
UserHistoryModule,
|
||||||
TeamModule,
|
TeamModule,
|
||||||
|
TeamEnvironmentsModule,
|
||||||
],
|
],
|
||||||
providers: [GQLComplexityPlugin],
|
providers: [GQLComplexityPlugin],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export const SHORTCODE_INVALID_JSON = 'shortcode/invalid_json' as const;
|
|||||||
* Invalid or non-existent TEAM ENVIRONMMENT ID
|
* Invalid or non-existent TEAM ENVIRONMMENT ID
|
||||||
* (TeamEnvironmentsService)
|
* (TeamEnvironmentsService)
|
||||||
*/
|
*/
|
||||||
export const TEAM_ENVIRONMMENT_NOT_FOUND =
|
export const TEAM_ENVIRONMENT_NOT_FOUND =
|
||||||
'team_environment/not_found' as const;
|
'team_environment/not_found' as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { UserSettings } from 'src/user-settings/user-settings.model';
|
|||||||
import { UserEnvironment } from '../user-environment/user-environments.model';
|
import { UserEnvironment } from '../user-environment/user-environments.model';
|
||||||
import { UserHistory } from '../user-history/user-history.model';
|
import { UserHistory } from '../user-history/user-history.model';
|
||||||
import { TeamMember } from 'src/team/team.model';
|
import { TeamMember } from 'src/team/team.model';
|
||||||
|
import { TeamEnvironment } from 'src/team-environments/team-environments.model';
|
||||||
|
|
||||||
// A custom message type that defines the topic and the corresponding payload.
|
// A custom message type that defines the topic and the corresponding payload.
|
||||||
// For every module that publishes a subscription add its type def and the possible subscription type.
|
// For every module that publishes a subscription add its type def and the possible subscription type.
|
||||||
@@ -19,5 +20,8 @@ export type TopicDef = {
|
|||||||
[topic: `team/${string}/member_removed`]: string;
|
[topic: `team/${string}/member_removed`]: string;
|
||||||
[topic: `team/${string}/member_added`]: TeamMember;
|
[topic: `team/${string}/member_added`]: TeamMember;
|
||||||
[topic: `team/${string}/member_updated`]: TeamMember;
|
[topic: `team/${string}/member_updated`]: TeamMember;
|
||||||
|
[topic: `team_environment/${string}/created`]: TeamEnvironment;
|
||||||
|
[topic: `team_environment/${string}/updated`]: TeamEnvironment;
|
||||||
|
[topic: `team_environment/${string}/deleted`]: TeamEnvironment;
|
||||||
[topic: `user_history/${string}/deleted_many`]: number;
|
[topic: `user_history/${string}/deleted_many`]: number;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import * as TE from 'fp-ts/TaskEither';
|
||||||
|
import * as O from 'fp-ts/Option';
|
||||||
|
import * as S from 'fp-ts/string';
|
||||||
|
import { pipe } from 'fp-ts/function';
|
||||||
|
import {
|
||||||
|
getAnnotatedRequiredRoles,
|
||||||
|
getGqlArg,
|
||||||
|
getUserFromGQLContext,
|
||||||
|
namedTrace,
|
||||||
|
throwErr,
|
||||||
|
} from 'src/utils';
|
||||||
|
import { TeamEnvironmentsService } from './team-environments.service';
|
||||||
|
import {
|
||||||
|
BUG_AUTH_NO_USER_CTX,
|
||||||
|
BUG_TEAM_ENV_GUARD_NO_ENV_ID,
|
||||||
|
BUG_TEAM_ENV_GUARD_NO_REQUIRE_ROLES,
|
||||||
|
TEAM_ENVIRONMENT_NOT_TEAM_MEMBER,
|
||||||
|
TEAM_ENVIRONMENT_NOT_FOUND,
|
||||||
|
} from 'src/errors';
|
||||||
|
import { TeamService } from 'src/team/team.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A guard which checks whether the caller of a GQL Operation
|
||||||
|
* is in the team which owns the environment.
|
||||||
|
* This guard also requires the RequireRole decorator for access control
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class GqlTeamEnvTeamGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private readonly reflector: Reflector,
|
||||||
|
private readonly teamEnvironmentService: TeamEnvironmentsService,
|
||||||
|
private readonly teamService: TeamService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
|
return pipe(
|
||||||
|
TE.Do,
|
||||||
|
|
||||||
|
TE.bindW('requiredRoles', () =>
|
||||||
|
pipe(
|
||||||
|
getAnnotatedRequiredRoles(this.reflector, context),
|
||||||
|
TE.fromOption(() => BUG_TEAM_ENV_GUARD_NO_REQUIRE_ROLES),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
TE.bindW('user', () =>
|
||||||
|
pipe(
|
||||||
|
getUserFromGQLContext(context),
|
||||||
|
TE.fromOption(() => BUG_AUTH_NO_USER_CTX),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
TE.bindW('envID', () =>
|
||||||
|
pipe(
|
||||||
|
getGqlArg('id', context),
|
||||||
|
O.fromPredicate(S.isString),
|
||||||
|
TE.fromOption(() => BUG_TEAM_ENV_GUARD_NO_ENV_ID),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
TE.bindW('membership', ({ envID, user }) =>
|
||||||
|
pipe(
|
||||||
|
this.teamEnvironmentService.getTeamEnvironment(envID),
|
||||||
|
TE.fromTaskOption(() => TEAM_ENVIRONMENT_NOT_FOUND),
|
||||||
|
TE.chainW((env) =>
|
||||||
|
pipe(
|
||||||
|
this.teamService.getTeamMemberTE(env.teamID, user.uid),
|
||||||
|
TE.mapLeft(() => TEAM_ENVIRONMENT_NOT_TEAM_MEMBER),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
TE.map(({ membership, requiredRoles }) =>
|
||||||
|
requiredRoles.includes(membership.role),
|
||||||
|
),
|
||||||
|
|
||||||
|
TE.getOrElse(throwErr),
|
||||||
|
)();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class TeamEnvironment {
|
||||||
|
@Field(() => ID, {
|
||||||
|
description: 'ID of the Team Environment',
|
||||||
|
})
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Field(() => ID, {
|
||||||
|
description: 'ID of the team this environment belongs to',
|
||||||
|
})
|
||||||
|
teamID: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
description: 'Name of the environment',
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
description: 'All variables present in the environment',
|
||||||
|
})
|
||||||
|
variables: string; // JSON string of the variables object (format:[{ key: "bla", value: "bla_val" }, ...] ) which will be received from the client
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TeamEnvironmentsService } from './team-environments.service';
|
||||||
|
import { TeamEnvironmentsResolver } from './team-environments.resolver';
|
||||||
|
import { UserModule } from 'src/user/user.module';
|
||||||
|
import { PubSubModule } from 'src/pubsub/pubsub.module';
|
||||||
|
import { TeamModule } from 'src/team/team.module';
|
||||||
|
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||||
|
import { GqlTeamEnvTeamGuard } from './gql-team-env-team.guard';
|
||||||
|
import { TeamEnvsTeamResolver } from './team.resolver';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [PrismaModule, PubSubModule, UserModule, TeamModule],
|
||||||
|
providers: [
|
||||||
|
TeamEnvironmentsResolver,
|
||||||
|
TeamEnvironmentsService,
|
||||||
|
GqlTeamEnvTeamGuard,
|
||||||
|
TeamEnvsTeamResolver,
|
||||||
|
],
|
||||||
|
exports: [TeamEnvironmentsService, GqlTeamEnvTeamGuard],
|
||||||
|
})
|
||||||
|
export class TeamEnvironmentsModule {}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
import { UseGuards } from '@nestjs/common';
|
||||||
|
import { Resolver, Mutation, Args, Subscription, ID } from '@nestjs/graphql';
|
||||||
|
import { pipe } from 'fp-ts/function';
|
||||||
|
import * as TE from 'fp-ts/TaskEither';
|
||||||
|
import { GqlAuthGuard } from 'src/guards/gql-auth.guard';
|
||||||
|
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||||
|
import { RequiresTeamRole } from 'src/team/decorators/requires-team-role.decorator';
|
||||||
|
import { GqlTeamMemberGuard } from 'src/team/guards/gql-team-member.guard';
|
||||||
|
import { TeamMemberRole } from 'src/team/team.model';
|
||||||
|
import { throwErr } from 'src/utils';
|
||||||
|
import { GqlTeamEnvTeamGuard } from './gql-team-env-team.guard';
|
||||||
|
import { TeamEnvironment } from './team-environments.model';
|
||||||
|
import { TeamEnvironmentsService } from './team-environments.service';
|
||||||
|
|
||||||
|
@Resolver(() => 'TeamEnvironment')
|
||||||
|
export class TeamEnvironmentsResolver {
|
||||||
|
constructor(
|
||||||
|
private readonly teamEnvironmentsService: TeamEnvironmentsService,
|
||||||
|
private readonly pubsub: PubSubService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/* Mutations */
|
||||||
|
|
||||||
|
@Mutation(() => TeamEnvironment, {
|
||||||
|
description: 'Create a new Team Environment for given Team ID',
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
||||||
|
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
|
||||||
|
createTeamEnvironment(
|
||||||
|
@Args({
|
||||||
|
name: 'name',
|
||||||
|
description: 'Name of the Team Environment',
|
||||||
|
})
|
||||||
|
name: string,
|
||||||
|
@Args({
|
||||||
|
name: 'teamID',
|
||||||
|
description: 'ID of the Team',
|
||||||
|
type: () => ID,
|
||||||
|
})
|
||||||
|
teamID: string,
|
||||||
|
@Args({
|
||||||
|
name: 'variables',
|
||||||
|
description: 'JSON string of the variables object',
|
||||||
|
})
|
||||||
|
variables: string,
|
||||||
|
): Promise<TeamEnvironment> {
|
||||||
|
return this.teamEnvironmentsService.createTeamEnvironment(
|
||||||
|
name,
|
||||||
|
teamID,
|
||||||
|
variables,
|
||||||
|
)();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation(() => Boolean, {
|
||||||
|
description: 'Delete a Team Environment for given Team ID',
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlTeamEnvTeamGuard)
|
||||||
|
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
|
||||||
|
deleteTeamEnvironment(
|
||||||
|
@Args({
|
||||||
|
name: 'id',
|
||||||
|
description: 'ID of the Team Environment',
|
||||||
|
type: () => ID,
|
||||||
|
})
|
||||||
|
id: string,
|
||||||
|
): Promise<boolean> {
|
||||||
|
return pipe(
|
||||||
|
this.teamEnvironmentsService.deleteTeamEnvironment(id),
|
||||||
|
TE.getOrElse(throwErr),
|
||||||
|
)();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation(() => TeamEnvironment, {
|
||||||
|
description:
|
||||||
|
'Add/Edit a single environment variable or variables to a Team Environment',
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlTeamEnvTeamGuard)
|
||||||
|
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
|
||||||
|
updateTeamEnvironment(
|
||||||
|
@Args({
|
||||||
|
name: 'id',
|
||||||
|
description: 'ID of the Team Environment',
|
||||||
|
type: () => ID,
|
||||||
|
})
|
||||||
|
id: string,
|
||||||
|
@Args({
|
||||||
|
name: 'name',
|
||||||
|
description: 'Name of the Team Environment',
|
||||||
|
})
|
||||||
|
name: string,
|
||||||
|
@Args({
|
||||||
|
name: 'variables',
|
||||||
|
description: 'JSON string of the variables object',
|
||||||
|
})
|
||||||
|
variables: string,
|
||||||
|
): Promise<TeamEnvironment> {
|
||||||
|
return pipe(
|
||||||
|
this.teamEnvironmentsService.updateTeamEnvironment(id, name, variables),
|
||||||
|
TE.getOrElse(throwErr),
|
||||||
|
)();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation(() => TeamEnvironment, {
|
||||||
|
description: 'Delete all variables from a Team Environment',
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlTeamEnvTeamGuard)
|
||||||
|
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
|
||||||
|
deleteAllVariablesFromTeamEnvironment(
|
||||||
|
@Args({
|
||||||
|
name: 'id',
|
||||||
|
description: 'ID of the Team Environment',
|
||||||
|
type: () => ID,
|
||||||
|
})
|
||||||
|
id: string,
|
||||||
|
): Promise<TeamEnvironment> {
|
||||||
|
return pipe(
|
||||||
|
this.teamEnvironmentsService.deleteAllVariablesFromTeamEnvironment(id),
|
||||||
|
TE.getOrElse(throwErr),
|
||||||
|
)();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation(() => TeamEnvironment, {
|
||||||
|
description: 'Create a duplicate of an existing environment',
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlTeamEnvTeamGuard)
|
||||||
|
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
|
||||||
|
createDuplicateEnvironment(
|
||||||
|
@Args({
|
||||||
|
name: 'id',
|
||||||
|
description: 'ID of the Team Environment',
|
||||||
|
type: () => ID,
|
||||||
|
})
|
||||||
|
id: string,
|
||||||
|
): Promise<TeamEnvironment> {
|
||||||
|
return pipe(
|
||||||
|
this.teamEnvironmentsService.createDuplicateEnvironment(id),
|
||||||
|
TE.getOrElse(throwErr),
|
||||||
|
)();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subscriptions */
|
||||||
|
|
||||||
|
@Subscription(() => TeamEnvironment, {
|
||||||
|
description: 'Listen for Team Environment Updates',
|
||||||
|
resolve: (value) => value,
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
||||||
|
@RequiresTeamRole(
|
||||||
|
TeamMemberRole.OWNER,
|
||||||
|
TeamMemberRole.EDITOR,
|
||||||
|
TeamMemberRole.VIEWER,
|
||||||
|
)
|
||||||
|
teamEnvironmentUpdated(
|
||||||
|
@Args({
|
||||||
|
name: 'teamID',
|
||||||
|
description: 'ID of the Team',
|
||||||
|
type: () => ID,
|
||||||
|
})
|
||||||
|
teamID: string,
|
||||||
|
) {
|
||||||
|
return this.pubsub.asyncIterator(`team_environment/${teamID}/updated`);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscription(() => TeamEnvironment, {
|
||||||
|
description: 'Listen for Team Environment Creation Messages',
|
||||||
|
resolve: (value) => value,
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
||||||
|
@RequiresTeamRole(
|
||||||
|
TeamMemberRole.OWNER,
|
||||||
|
TeamMemberRole.EDITOR,
|
||||||
|
TeamMemberRole.VIEWER,
|
||||||
|
)
|
||||||
|
teamEnvironmentCreated(
|
||||||
|
@Args({
|
||||||
|
name: 'teamID',
|
||||||
|
description: 'ID of the Team',
|
||||||
|
type: () => ID,
|
||||||
|
})
|
||||||
|
teamID: string,
|
||||||
|
) {
|
||||||
|
return this.pubsub.asyncIterator(`team_environment/${teamID}/created`);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscription(() => TeamEnvironment, {
|
||||||
|
description: 'Listen for Team Environment Deletion Messages',
|
||||||
|
resolve: (value) => value,
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
||||||
|
@RequiresTeamRole(
|
||||||
|
TeamMemberRole.OWNER,
|
||||||
|
TeamMemberRole.EDITOR,
|
||||||
|
TeamMemberRole.VIEWER,
|
||||||
|
)
|
||||||
|
teamEnvironmentDeleted(
|
||||||
|
@Args({
|
||||||
|
name: 'teamID',
|
||||||
|
description: 'ID of the Team',
|
||||||
|
type: () => ID,
|
||||||
|
})
|
||||||
|
teamID: string,
|
||||||
|
) {
|
||||||
|
return this.pubsub.asyncIterator(`team_environment/${teamID}/deleted`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,403 @@
|
|||||||
|
import { mockDeep, mockReset } from 'jest-mock-extended';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
import { TeamEnvironment } from './team-environments.model';
|
||||||
|
import { TeamEnvironmentsService } from './team-environments.service';
|
||||||
|
import { TEAM_ENVIRONMENT_NOT_FOUND, TEAM_MEMBER_NOT_FOUND } from 'src/errors';
|
||||||
|
|
||||||
|
const mockPrisma = mockDeep<PrismaService>();
|
||||||
|
|
||||||
|
const mockPubSub = {
|
||||||
|
publish: jest.fn().mockResolvedValue(null),
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
const teamEnvironmentsService = new TeamEnvironmentsService(
|
||||||
|
mockPrisma,
|
||||||
|
mockPubSub as any,
|
||||||
|
);
|
||||||
|
|
||||||
|
const teamEnvironment = {
|
||||||
|
id: '123',
|
||||||
|
name: 'test',
|
||||||
|
teamID: 'abc123',
|
||||||
|
variables: [{}],
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockReset(mockPrisma);
|
||||||
|
mockPubSub.publish.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TeamEnvironmentsService', () => {
|
||||||
|
describe('getTeamEnvironment', () => {
|
||||||
|
test('queries the db with the id', async () => {
|
||||||
|
mockPrisma.teamEnvironment.findFirst.mockResolvedValue(teamEnvironment);
|
||||||
|
|
||||||
|
await teamEnvironmentsService.getTeamEnvironment('123')();
|
||||||
|
|
||||||
|
expect(mockPrisma.teamEnvironment.findFirst).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
where: {
|
||||||
|
id: '123',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('requests prisma to reject the query promise if not found', async () => {
|
||||||
|
mockPrisma.teamEnvironment.findFirst.mockResolvedValue(teamEnvironment);
|
||||||
|
|
||||||
|
await teamEnvironmentsService.getTeamEnvironment('123')();
|
||||||
|
|
||||||
|
expect(mockPrisma.teamEnvironment.findFirst).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
rejectOnNotFound: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return a Some of the correct environment if exists', async () => {
|
||||||
|
mockPrisma.teamEnvironment.findFirst.mockResolvedValue(teamEnvironment);
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.getTeamEnvironment('123')();
|
||||||
|
|
||||||
|
expect(result).toEqualSome(teamEnvironment);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return a None if the environment does not exist', async () => {
|
||||||
|
mockPrisma.teamEnvironment.findFirst.mockRejectedValue('NotFoundError');
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.getTeamEnvironment('123')();
|
||||||
|
|
||||||
|
expect(result).toBeNone();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('createTeamEnvironment', () => {
|
||||||
|
test('should create and return a new team environment given a valid name,variable and team ID', async () => {
|
||||||
|
mockPrisma.teamEnvironment.create.mockResolvedValue(teamEnvironment);
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.createTeamEnvironment(
|
||||||
|
teamEnvironment.name,
|
||||||
|
teamEnvironment.teamID,
|
||||||
|
JSON.stringify(teamEnvironment.variables),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqual(<TeamEnvironment>{
|
||||||
|
id: teamEnvironment.id,
|
||||||
|
name: teamEnvironment.name,
|
||||||
|
teamID: teamEnvironment.teamID,
|
||||||
|
variables: JSON.stringify(teamEnvironment.variables),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should reject if given team ID is invalid', async () => {
|
||||||
|
mockPrisma.teamEnvironment.create.mockRejectedValue(null as any);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
teamEnvironmentsService.createTeamEnvironment(
|
||||||
|
teamEnvironment.name,
|
||||||
|
'invalidteamid',
|
||||||
|
JSON.stringify(teamEnvironment.variables),
|
||||||
|
),
|
||||||
|
).rejects.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should reject if provided team environment name is not a string', async () => {
|
||||||
|
mockPrisma.teamEnvironment.create.mockRejectedValue(null as any);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
teamEnvironmentsService.createTeamEnvironment(
|
||||||
|
null as any,
|
||||||
|
teamEnvironment.teamID,
|
||||||
|
JSON.stringify(teamEnvironment.variables),
|
||||||
|
),
|
||||||
|
).rejects.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should reject if provided variable is not a string', async () => {
|
||||||
|
mockPrisma.teamEnvironment.create.mockRejectedValue(null as any);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
teamEnvironmentsService.createTeamEnvironment(
|
||||||
|
teamEnvironment.name,
|
||||||
|
teamEnvironment.teamID,
|
||||||
|
null as any,
|
||||||
|
),
|
||||||
|
).rejects.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should send pubsub message to "team_environment/<teamID>/created" if team environment is created successfully', async () => {
|
||||||
|
mockPrisma.teamEnvironment.create.mockResolvedValueOnce(teamEnvironment);
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.createTeamEnvironment(
|
||||||
|
teamEnvironment.name,
|
||||||
|
teamEnvironment.teamID,
|
||||||
|
JSON.stringify(teamEnvironment.variables),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||||
|
`team_environment/${teamEnvironment.teamID}/created`,
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteTeamEnvironment', () => {
|
||||||
|
test('should resolve to true given a valid team environment ID', async () => {
|
||||||
|
mockPrisma.teamEnvironment.delete.mockResolvedValueOnce(teamEnvironment);
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.deleteTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualRight(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw TEAM_ENVIRONMMENT_NOT_FOUND if given id is invalid', async () => {
|
||||||
|
mockPrisma.teamEnvironment.delete.mockRejectedValue('RecordNotFound');
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.deleteTeamEnvironment(
|
||||||
|
'invalidid',
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualLeft(TEAM_ENVIRONMENT_NOT_FOUND);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should send pubsub message to "team_environment/<teamID>/deleted" if team environment is deleted successfully', async () => {
|
||||||
|
mockPrisma.teamEnvironment.delete.mockResolvedValueOnce(teamEnvironment);
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.deleteTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||||
|
`team_environment/${teamEnvironment.teamID}/deleted`,
|
||||||
|
{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify(teamEnvironment.variables),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateVariablesInTeamEnvironment', () => {
|
||||||
|
test('should add new variable to a team environment', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockResolvedValueOnce({
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: [{ key: 'value' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.updateTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
teamEnvironment.name,
|
||||||
|
JSON.stringify([{ key: 'value' }]),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualRight(<TeamEnvironment>{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify([{ key: 'value' }]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should add new variable to already existing list of variables in a team environment', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockResolvedValueOnce({
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: [{ key: 'value' }, { key_2: 'value_2' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.updateTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
teamEnvironment.name,
|
||||||
|
JSON.stringify([{ key: 'value' }, { key_2: 'value_2' }]),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualRight(<TeamEnvironment>{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify([{ key: 'value' }, { key_2: 'value_2' }]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should edit existing variables in a team environment', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockResolvedValueOnce({
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: [{ key: '1234' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.updateTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
teamEnvironment.name,
|
||||||
|
JSON.stringify([{ key: '1234' }]),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualRight(<TeamEnvironment>{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify([{ key: '1234' }]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should delete existing variable in a team environment', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockResolvedValueOnce(teamEnvironment);
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.updateTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
teamEnvironment.name,
|
||||||
|
JSON.stringify([{}]),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualRight(<TeamEnvironment>{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify([{}]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should edit name of an existing team environment', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockResolvedValueOnce({
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: [{ key: '123' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.updateTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
teamEnvironment.name,
|
||||||
|
JSON.stringify([{ key: '123' }]),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualRight(<TeamEnvironment>{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify([{ key: '123' }]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should reject to TEAM_ENVIRONMMENT_NOT_FOUND if provided id is invalid', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockRejectedValue('RecordNotFound');
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.updateTeamEnvironment(
|
||||||
|
'invalidid',
|
||||||
|
teamEnvironment.name,
|
||||||
|
JSON.stringify(teamEnvironment.variables),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualLeft(TEAM_ENVIRONMENT_NOT_FOUND);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should send pubsub message to "team_environment/<teamID>/updated" if team environment is updated successfully', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockResolvedValueOnce(teamEnvironment);
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.updateTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
teamEnvironment.name,
|
||||||
|
JSON.stringify([{ key: 'value' }]),
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||||
|
`team_environment/${teamEnvironment.teamID}/updated`,
|
||||||
|
{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify(teamEnvironment.variables),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteAllVariablesFromTeamEnvironment', () => {
|
||||||
|
test('should delete all variables in a team environment', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockResolvedValueOnce(teamEnvironment);
|
||||||
|
|
||||||
|
const result =
|
||||||
|
await teamEnvironmentsService.deleteAllVariablesFromTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualRight(<TeamEnvironment>{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify([{}]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should reject to TEAM_ENVIRONMMENT_NOT_FOUND if provided id is invalid', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockRejectedValue('RecordNotFound');
|
||||||
|
|
||||||
|
const result =
|
||||||
|
await teamEnvironmentsService.deleteAllVariablesFromTeamEnvironment(
|
||||||
|
'invalidid',
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualLeft(TEAM_ENVIRONMENT_NOT_FOUND);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should send pubsub message to "team_environment/<teamID>/updated" if team environment is updated successfully', async () => {
|
||||||
|
mockPrisma.teamEnvironment.update.mockResolvedValueOnce(teamEnvironment);
|
||||||
|
|
||||||
|
const result =
|
||||||
|
await teamEnvironmentsService.deleteAllVariablesFromTeamEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||||
|
`team_environment/${teamEnvironment.teamID}/updated`,
|
||||||
|
{
|
||||||
|
...teamEnvironment,
|
||||||
|
variables: JSON.stringify([{}]),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createDuplicateEnvironment', () => {
|
||||||
|
test('should duplicate an existing team environment', async () => {
|
||||||
|
mockPrisma.teamEnvironment.findFirst.mockResolvedValueOnce(
|
||||||
|
teamEnvironment,
|
||||||
|
);
|
||||||
|
|
||||||
|
mockPrisma.teamEnvironment.create.mockResolvedValueOnce({
|
||||||
|
...teamEnvironment,
|
||||||
|
id: 'newid',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.createDuplicateEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualRight(<TeamEnvironment>{
|
||||||
|
...teamEnvironment,
|
||||||
|
id: 'newid',
|
||||||
|
variables: JSON.stringify(teamEnvironment.variables),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should reject to TEAM_ENVIRONMMENT_NOT_FOUND if provided id is invalid', async () => {
|
||||||
|
mockPrisma.teamEnvironment.findFirst.mockRejectedValue('NotFoundError');
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.createDuplicateEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(result).toEqualLeft(TEAM_ENVIRONMENT_NOT_FOUND);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should send pubsub message to "team_environment/<teamID>/created" if team environment is updated successfully', async () => {
|
||||||
|
mockPrisma.teamEnvironment.findFirst.mockResolvedValueOnce(
|
||||||
|
teamEnvironment,
|
||||||
|
);
|
||||||
|
|
||||||
|
mockPrisma.teamEnvironment.create.mockResolvedValueOnce({
|
||||||
|
...teamEnvironment,
|
||||||
|
id: 'newid',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await teamEnvironmentsService.createDuplicateEnvironment(
|
||||||
|
teamEnvironment.id,
|
||||||
|
)();
|
||||||
|
|
||||||
|
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||||
|
`team_environment/${teamEnvironment.teamID}/created`,
|
||||||
|
{
|
||||||
|
...teamEnvironment,
|
||||||
|
id: 'newid',
|
||||||
|
variables: JSON.stringify([{}]),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { pipe } from 'fp-ts/function';
|
||||||
|
import * as T from 'fp-ts/Task';
|
||||||
|
import * as TO from 'fp-ts/TaskOption';
|
||||||
|
import * as TE from 'fp-ts/TaskEither';
|
||||||
|
import * as A from 'fp-ts/Array';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||||
|
import { TeamEnvironment } from './team-environments.model';
|
||||||
|
import { TEAM_ENVIRONMENT_NOT_FOUND } from 'src/errors';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TeamEnvironmentsService {
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly pubsub: PubSubService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
getTeamEnvironment(id: string) {
|
||||||
|
return TO.tryCatch(() =>
|
||||||
|
this.prisma.teamEnvironment.findFirst({
|
||||||
|
where: { id },
|
||||||
|
rejectOnNotFound: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createTeamEnvironment(name: string, teamID: string, variables: string) {
|
||||||
|
return pipe(
|
||||||
|
() =>
|
||||||
|
this.prisma.teamEnvironment.create({
|
||||||
|
data: {
|
||||||
|
name: name,
|
||||||
|
teamID: teamID,
|
||||||
|
variables: JSON.parse(variables),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
T.chainFirst(
|
||||||
|
(environment) => () =>
|
||||||
|
this.pubsub.publish(
|
||||||
|
`team_environment/${environment.teamID}/created`,
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
T.map((data) => {
|
||||||
|
return <TeamEnvironment>{
|
||||||
|
id: data.id,
|
||||||
|
name: data.name,
|
||||||
|
teamID: data.teamID,
|
||||||
|
variables: JSON.stringify(data.variables),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTeamEnvironment(id: string) {
|
||||||
|
return pipe(
|
||||||
|
TE.tryCatch(
|
||||||
|
() =>
|
||||||
|
this.prisma.teamEnvironment.delete({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
() => TEAM_ENVIRONMENT_NOT_FOUND,
|
||||||
|
),
|
||||||
|
TE.chainFirst((environment) =>
|
||||||
|
TE.fromTask(() =>
|
||||||
|
this.pubsub.publish(
|
||||||
|
`team_environment/${environment.teamID}/deleted`,
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TE.map((data) => true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTeamEnvironment(id: string, name: string, variables: string) {
|
||||||
|
return pipe(
|
||||||
|
TE.tryCatch(
|
||||||
|
() =>
|
||||||
|
this.prisma.teamEnvironment.update({
|
||||||
|
where: { id: id },
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
variables: JSON.parse(variables),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
() => TEAM_ENVIRONMENT_NOT_FOUND,
|
||||||
|
),
|
||||||
|
TE.chainFirst((environment) =>
|
||||||
|
TE.fromTask(() =>
|
||||||
|
this.pubsub.publish(
|
||||||
|
`team_environment/${environment.teamID}/updated`,
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TE.map(
|
||||||
|
(environment) =>
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAllVariablesFromTeamEnvironment(id: string) {
|
||||||
|
return pipe(
|
||||||
|
TE.tryCatch(
|
||||||
|
() =>
|
||||||
|
this.prisma.teamEnvironment.update({
|
||||||
|
where: { id: id },
|
||||||
|
data: {
|
||||||
|
variables: [],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
() => TEAM_ENVIRONMENT_NOT_FOUND,
|
||||||
|
),
|
||||||
|
TE.chainFirst((environment) =>
|
||||||
|
TE.fromTask(() =>
|
||||||
|
this.pubsub.publish(
|
||||||
|
`team_environment/${environment.teamID}/updated`,
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TE.map(
|
||||||
|
(environment) =>
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createDuplicateEnvironment(id: string) {
|
||||||
|
return pipe(
|
||||||
|
TE.tryCatch(
|
||||||
|
() =>
|
||||||
|
this.prisma.teamEnvironment.findFirst({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
rejectOnNotFound: true,
|
||||||
|
}),
|
||||||
|
() => TEAM_ENVIRONMENT_NOT_FOUND,
|
||||||
|
),
|
||||||
|
TE.chain((environment) =>
|
||||||
|
TE.fromTask(() =>
|
||||||
|
this.prisma.teamEnvironment.create({
|
||||||
|
data: {
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: environment.variables as Prisma.JsonArray,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TE.chainFirst((environment) =>
|
||||||
|
TE.fromTask(() =>
|
||||||
|
this.pubsub.publish(
|
||||||
|
`team_environment/${environment.teamID}/created`,
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TE.map(
|
||||||
|
(environment) =>
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAllTeamEnvironments(teamID: string) {
|
||||||
|
return pipe(
|
||||||
|
() =>
|
||||||
|
this.prisma.teamEnvironment.findMany({
|
||||||
|
where: {
|
||||||
|
teamID: teamID,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
T.map(
|
||||||
|
A.map(
|
||||||
|
(environment) =>
|
||||||
|
<TeamEnvironment>{
|
||||||
|
id: environment.id,
|
||||||
|
name: environment.name,
|
||||||
|
teamID: environment.teamID,
|
||||||
|
variables: JSON.stringify(environment.variables),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
|
||||||
|
import { Team } from 'src/team/team.model';
|
||||||
|
import { TeamEnvironment } from './team-environments.model';
|
||||||
|
import { TeamEnvironmentsService } from './team-environments.service';
|
||||||
|
|
||||||
|
@Resolver(() => Team)
|
||||||
|
export class TeamEnvsTeamResolver {
|
||||||
|
constructor(private teamEnvironmentService: TeamEnvironmentsService) {}
|
||||||
|
|
||||||
|
@ResolveField(() => [TeamEnvironment], {
|
||||||
|
description: 'Returns all Team Environments for the given Team',
|
||||||
|
})
|
||||||
|
teamEnvironments(@Parent() team: Team): Promise<TeamEnvironment[]> {
|
||||||
|
return this.teamEnvironmentService.fetchAllTeamEnvironments(team.id)();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
BUG_AUTH_NO_USER_CTX,
|
BUG_AUTH_NO_USER_CTX,
|
||||||
BUG_TEAM_NO_REQUIRE_TEAM_ROLE,
|
BUG_TEAM_NO_REQUIRE_TEAM_ROLE,
|
||||||
BUG_TEAM_NO_TEAM_ID,
|
BUG_TEAM_NO_TEAM_ID,
|
||||||
|
TEAM_MEMBER_NOT_FOUND,
|
||||||
} from 'src/errors';
|
} from 'src/errors';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -23,24 +24,20 @@ export class GqlTeamMemberGuard implements CanActivate {
|
|||||||
'requiresTeamRole',
|
'requiresTeamRole',
|
||||||
context.getHandler(),
|
context.getHandler(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!requireRoles) throw new Error(BUG_TEAM_NO_REQUIRE_TEAM_ROLE);
|
if (!requireRoles) throw new Error(BUG_TEAM_NO_REQUIRE_TEAM_ROLE);
|
||||||
|
|
||||||
const gqlExecCtx = GqlExecutionContext.create(context);
|
const gqlExecCtx = GqlExecutionContext.create(context);
|
||||||
|
|
||||||
const { user } = gqlExecCtx.getContext().req;
|
const { user } = gqlExecCtx.getContext().req;
|
||||||
|
|
||||||
if (user == undefined) throw new Error(BUG_AUTH_NO_USER_CTX);
|
if (user == undefined) throw new Error(BUG_AUTH_NO_USER_CTX);
|
||||||
|
|
||||||
const { teamID } = gqlExecCtx.getArgs<{ teamID: string }>();
|
const { teamID } = gqlExecCtx.getArgs<{ teamID: string }>();
|
||||||
|
|
||||||
if (!teamID) throw new Error(BUG_TEAM_NO_TEAM_ID);
|
if (!teamID) throw new Error(BUG_TEAM_NO_TEAM_ID);
|
||||||
|
|
||||||
const status = await this.teamService.getTeamMember(teamID, user.uid);
|
const teamMember = await this.teamService.getTeamMember(teamID, user.uid);
|
||||||
|
if (!teamMember) throw new Error(TEAM_MEMBER_NOT_FOUND);
|
||||||
|
|
||||||
if (!status) throw new Error('team/member_not_found');
|
if (requireRoles.includes(teamMember.role)) return true;
|
||||||
|
|
||||||
if (requireRoles.includes(status.role)) return true;
|
|
||||||
|
|
||||||
throw new Error(TEAM_NOT_REQUIRED_ROLE);
|
throw new Error(TEAM_NOT_REQUIRED_ROLE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export class Team {
|
|||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class TeamMember {
|
export class TeamMember {
|
||||||
@Field(() => ID, {
|
@Field(() => ID, {
|
||||||
description: 'Membership ID of the Team Member'
|
description: 'Membership ID of the Team Member',
|
||||||
})
|
})
|
||||||
membershipID: string;
|
membershipID: string;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { PrismaModule } from '../prisma/prisma.module';
|
|||||||
import { PubSubModule } from '../pubsub/pubsub.module';
|
import { PubSubModule } from '../pubsub/pubsub.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UserModule ,PubSubModule , PrismaModule ],
|
imports: [UserModule, PubSubModule, PrismaModule],
|
||||||
providers: [
|
providers: [
|
||||||
TeamService,
|
TeamService,
|
||||||
TeamResolver,
|
TeamResolver,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
|||||||
role: TeamMemberRole,
|
role: TeamMemberRole,
|
||||||
): Promise<E.Left<string> | E.Right<TeamMember>> {
|
): Promise<E.Left<string> | E.Right<TeamMember>> {
|
||||||
const user = await this.userService.findUserByEmail(email);
|
const user = await this.userService.findUserByEmail(email);
|
||||||
if(O.isNone(user)) return E.left(USER_NOT_FOUND);
|
if (O.isNone(user)) return E.left(USER_NOT_FOUND);
|
||||||
|
|
||||||
const teamMember = await this.addMemberToTeam(teamID, user.value.uid, role);
|
const teamMember = await this.addMemberToTeam(teamID, user.value.uid, role);
|
||||||
return E.right(teamMember);
|
return E.right(teamMember);
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { ExecutionContext } from '@nestjs/common';
|
import { ExecutionContext } from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||||
import { pipe } from 'fp-ts/lib/function';
|
import { pipe } from 'fp-ts/lib/function';
|
||||||
import * as O from 'fp-ts/Option';
|
import * as O from 'fp-ts/Option';
|
||||||
import * as TE from 'fp-ts/TaskEither';
|
import * as TE from 'fp-ts/TaskEither';
|
||||||
import * as T from 'fp-ts/Task';
|
import * as T from 'fp-ts/Task';
|
||||||
import * as E from 'fp-ts/Either';
|
import * as E from 'fp-ts/Either';
|
||||||
import { User } from './user/user.model';
|
|
||||||
import * as A from 'fp-ts/Array';
|
import * as A from 'fp-ts/Array';
|
||||||
|
import { TeamMemberRole } from './team/team.model';
|
||||||
|
import { User } from './user/user.model';
|
||||||
import { JSON_INVALID } from './errors';
|
import { JSON_INVALID } from './errors';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,14 +53,14 @@ export const namedTrace =
|
|||||||
* @param context NestJS Execution Context
|
* @param context NestJS Execution Context
|
||||||
* @returns An Option which contains the defined roles
|
* @returns An Option which contains the defined roles
|
||||||
*/
|
*/
|
||||||
// export const getAnnotatedRequiredRoles = (
|
export const getAnnotatedRequiredRoles = (
|
||||||
// reflector: Reflector,
|
reflector: Reflector,
|
||||||
// context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
// ) =>
|
) =>
|
||||||
// pipe(
|
pipe(
|
||||||
// reflector.get<TeamMemberRole[]>('requiresTeamRole', context.getHandler()),
|
reflector.get<TeamMemberRole[]>('requiresTeamRole', context.getHandler()),
|
||||||
// O.fromNullable,
|
O.fromNullable,
|
||||||
// );
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the user from the NestJS GQL Execution Context.
|
* Gets the user from the NestJS GQL Execution Context.
|
||||||
@@ -70,7 +72,7 @@ export const getUserFromGQLContext = (ctx: ExecutionContext) =>
|
|||||||
pipe(
|
pipe(
|
||||||
ctx,
|
ctx,
|
||||||
GqlExecutionContext.create,
|
GqlExecutionContext.create,
|
||||||
(ctx) => ctx.getContext<{ user?: User }>(),
|
(ctx) => ctx.getContext().req,
|
||||||
({ user }) => user,
|
({ user }) => user,
|
||||||
O.fromNullable,
|
O.fromNullable,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user