Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: v2 event types endpoint paths #14545

Merged
merged 9 commits into from
Apr 13, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { AppModule } from "@/app.module";
import { EventTypesModule } from "@/ee/event-types/event-types.module";
import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
import { UpdateEventTypeInput } from "@/ee/event-types/inputs/update-event-type.input";
import { GetEventTypePublicOutput } from "@/ee/event-types/outputs/get-event-type-public.output";
import { GetEventTypeOutput } from "@/ee/event-types/outputs/get-event-type.output";
import { GetEventTypesPublicOutput } from "@/ee/event-types/outputs/get-event-types-public.output";
import { HttpExceptionFilter } from "@/filters/http-exception.filter";
import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
Expand Down Expand Up @@ -64,7 +66,7 @@ describe("Event types Endpoints", () => {
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;

const userEmail = "event-types-test-e2e@api.com";
const name = "bob the builder";
const name = "bob-the-builder";
const username = name;
let eventType: EventType;
let user: User;
Expand Down Expand Up @@ -183,6 +185,41 @@ describe("Event types Endpoints", () => {
expect(responseBody.data.eventType.userId).toEqual(user.id);
});

it(`/GET/:username/public`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types/${username}/public`)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
.set("Authorization", `Bearer whatever`)
.expect(200);

const responseBody: GetEventTypesPublicOutput = response.body;

expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseBody.data?.length).toEqual(1);
expect(responseBody.data?.[0]?.id).toEqual(eventType.id);
expect(responseBody.data?.[0]?.title).toEqual(eventType.title);
expect(responseBody.data?.[0]?.slug).toEqual(eventType.slug);
expect(responseBody.data?.[0]?.length).toEqual(eventType.length);
});

it(`/GET/:username/:eventSlug/public`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types/${username}/${eventType.slug}/public`)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
.set("Authorization", `Bearer whatever`)
.expect(200);

const responseBody: GetEventTypePublicOutput = response.body;

expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseBody.data?.id).toEqual(eventType.id);
expect(responseBody.data?.title).toEqual(eventType.title);
expect(responseBody.data?.slug).toEqual(eventType.slug);
expect(responseBody.data?.length).toEqual(eventType.length);
});

it(`/GET/`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types`)
Expand All @@ -202,7 +239,7 @@ describe("Event types Endpoints", () => {
expect(responseBody.data.profiles?.[0]?.name).toEqual(name);
});

it(`/GET/:username/public`, async () => {
it(`/GET/public/:username/`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types/${username}/public`)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
import { GetPublicEventTypeQueryParams } from "@/ee/event-types/inputs/get-public-event-type-query-params.input";
import { UpdateEventTypeInput } from "@/ee/event-types/inputs/update-event-type.input";
import { CreateEventTypeOutput } from "@/ee/event-types/outputs/create-event-type.output";
import { DeleteEventTypeOutput } from "@/ee/event-types/outputs/delete-event-type.output";
import { GetEventTypePublicOutput } from "@/ee/event-types/outputs/get-event-type-public.output";
import { GetEventTypeOutput } from "@/ee/event-types/outputs/get-event-type.output";
import { GetEventTypesPublicOutput } from "@/ee/event-types/outputs/get-event-types-public.output";
import { GetEventTypesOutput } from "@/ee/event-types/outputs/get-event-types.output";
Expand All @@ -11,6 +13,7 @@ import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import {
Controller,
Expand All @@ -24,11 +27,15 @@ import {
HttpCode,
HttpStatus,
Delete,
Query,
InternalServerErrorException,
} from "@nestjs/common";
import { ApiTags as DocsTags } from "@nestjs/swagger";

import { EVENT_TYPE_READ, EVENT_TYPE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
import { getPublicEvent } from "@calcom/platform-libraries";
import { getEventTypesByViewer } from "@calcom/platform-libraries";
import { PrismaClient } from "@calcom/prisma";

@Controller({
path: "event-types",
Expand All @@ -37,7 +44,10 @@ import { getEventTypesByViewer } from "@calcom/platform-libraries";
@UseGuards(PermissionsGuard)
@DocsTags("Event types")
export class EventTypesController {
constructor(private readonly eventTypesService: EventTypesService) {}
constructor(
private readonly eventTypesService: EventTypesService,
private readonly prismaReadService: PrismaReadService
) {}

@Post("/")
@Permissions([EVENT_TYPE_WRITE])
Expand Down Expand Up @@ -90,6 +100,35 @@ export class EventTypesController {
};
}

@Get("/:username/:eventSlug/public")
async getPublicEventType(
@Param("username") username: string,
@Param("eventSlug") eventSlug: string,
@Query() queryParams: GetPublicEventTypeQueryParams
): Promise<GetEventTypePublicOutput> {
try {
const event = await getPublicEvent(
username.toLowerCase(),
eventSlug,
queryParams.isTeamEvent,
queryParams.org || null,
this.prismaReadService.prisma as unknown as PrismaClient,
// We should be fine allowing unpublished orgs events to be servable through platform because Platform access is behind license
// If there is ever a need to restrict this, we can introduce a new query param `fromRedirectOfNonOrgLink`
true
);
return {
data: event,
status: SUCCESS_STATUS,
};
} catch (err) {
if (err instanceof Error) {
throw new NotFoundException(err.message);
}
}
throw new InternalServerErrorException("Could not find public event.");
}

@Get("/:username/public")
async getPublicEventTypes(@Param("username") username: string): Promise<GetEventTypesPublicOutput> {
const eventTypes = await this.eventTypesService.getEventTypesPublicByUsername(username);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,7 @@ import { ApiProperty } from "@nestjs/swagger";
import { Transform } from "class-transformer";
import { IsBoolean, IsOptional, IsString } from "class-validator";

export class GetPublicEventInput {
@IsString()
@Transform(({ value }: { value: string }) => value.toLowerCase())
@ApiProperty({ required: true })
username!: string;

@IsString()
@ApiProperty({ required: true })
eventSlug!: string;

export class GetPublicEventTypeQueryParams {
@Transform(({ value }: { value: string }) => value === "true")
@IsBoolean()
@IsOptional()
Expand Down