Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/api-v2/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PrismaService } from "./common/db/prisma.service";
import { AuthGuard } from "./common/guards/auth.guard";
import { ApplicationsModule } from "./sections/applications/applications.module";
import { AuthModule } from "./sections/auth/auth.module";
import { MembersModule } from "./sections/members/members.module";
import { StatusModule } from "./sections/status/status.module";
import { UtilityModule } from "./sections/utility/utility.module";

Expand All @@ -14,6 +15,7 @@ import { UtilityModule } from "./sections/utility/utility.module";
AuthModule,
ApplicationsModule,
StatusModule,
MembersModule,
ConfigModule.forRoot({ isGlobal: true, cache: true }),
],
providers: [PrismaService, { provide: APP_GUARD, useClass: AuthGuard }],
Expand Down
7 changes: 3 additions & 4 deletions apps/api-v2/src/common/db/external/cachet.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ export class CachetAPIService {
} else {
this.baseURL = this.configService.get<string>("CACHET_URL") as string;
this.apiToken = this.configService.get<string>("CACHET_TOKEN") as string;
this.testConnection().then(() => {
this.logger.log("Cachet API is reachable");
});
}

this.testConnection().then(() => {
this.logger.log("Cachet API is reachable");
});
}

async testConnection(): Promise<string> {
Expand Down
27 changes: 27 additions & 0 deletions apps/api-v2/src/sections/members/dto/member.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ApiProperty } from "@nestjs/swagger";

export class MemberDto {
@ApiProperty({
example: "00000000-0000-0000-0000-000000000000",
description: "The unique ID of the user.",
})
id: string;

@ApiProperty({
example: "000000000000000000",
description: "The discord ID of the user.",
})
discordId: string;

@ApiProperty({
example: "myMinecraftName",
description: "The Minecraft username of the user.",
})
minecraft: string;

@ApiProperty({
example: "myUsername",
description: "The username of the user.",
})
username: string;
}
68 changes: 68 additions & 0 deletions apps/api-v2/src/sections/members/members.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Controller, Get, Req } from "@nestjs/common";
import { ApiBearerAuth, ApiOperation } from "@nestjs/swagger";
import { Request } from "express";
import {
ApiErrorResponse,
ApiPaginatedResponseDto,
} from "src/common/decorators/api-response.decorator";
import { Filter, FilterParams } from "src/common/decorators/filter.decorator";
import { Filtered } from "src/common/decorators/filtered.decorator";
import { Paginated } from "src/common/decorators/paginated.decorator";
import {
Pagination,
PaginationParams,
} from "src/common/decorators/pagination.decorator";
import { Sortable } from "src/common/decorators/sortable.decorator";
import {
Sorting,
SortingParams,
} from "src/common/decorators/sorting.decorator";
import { PaginatedControllerResponse } from "src/typings";
import { MemberDto } from "./dto/member.dto";
import { MembersService } from "./members.service";

@Controller("members")
export class MembersController {
constructor(private readonly membersService: MembersService) {}

@Get("/")
@ApiBearerAuth()
@Sortable({
defaultSortBy: "id",
allowedFields: ["id", "discordId", "minecraft", "username"],
defaultOrder: "desc",
})
@Paginated()
@Filtered({
fields: [
{ name: "id", required: false, type: String },
{ name: "discordId", required: false, type: String },
{
name: "minecraft",
required: false,
type: String,
},
{ name: "username", required: false, type: String },
],
})
@ApiOperation({
summary: "Get All Members",
description: "Returns all members of the currently authenticated team.",
})
@ApiPaginatedResponseDto(MemberDto, { description: "Success" })
@ApiErrorResponse({ status: 401, description: "Unauthorized" })
async getMembers(
@Pagination() pagination: PaginationParams,
@Sorting() sorting: SortingParams,
@Filter() filter: FilterParams,
@Req() req: Request,
): PaginatedControllerResponse {
return await this.membersService.findAll(
req.token.id,
pagination,
sorting.sortBy,
sorting.order,
filter.filter,
);
}
}
10 changes: 10 additions & 0 deletions apps/api-v2/src/sections/members/members.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from "@nestjs/common";
import { PrismaService } from "src/common/db/prisma.service";
import { MembersController } from "./members.controller";
import { MembersService } from "./members.service";

@Module({
controllers: [MembersController],
providers: [MembersService, PrismaService],
})
export class MembersModule {}
52 changes: 52 additions & 0 deletions apps/api-v2/src/sections/members/members.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Injectable } from "@nestjs/common";
import { PrismaService } from "src/common/db/prisma.service";
import { FilterParams } from "src/common/decorators/filter.decorator";
import { PaginationParams } from "src/common/decorators/pagination.decorator";
import { SortingParams } from "src/common/decorators/sorting.decorator";

@Injectable()
export class MembersService {
constructor(private readonly prisma: PrismaService) {}

async findAll(
buildteamId: string,
pagination: PaginationParams,
sortBy?: SortingParams["sortBy"],
order?: SortingParams["order"],
filter?: FilterParams["filter"],
) {
const take = Math.max(Number(pagination.limit) || 20, 1);
const skip = Math.max((Number(pagination.page) || 1) - 1, 0) * take;

const combinedFilter = {
...filter,
...{ joinedBuildTeams: { some: { id: buildteamId } } },
};

const [users, count] = await Promise.all([
this.prisma.user.findMany({
where: combinedFilter,
orderBy: { [sortBy || "id"]: order === "desc" ? "desc" : "asc" },
skip,
take,
select: {
id: true,
discordId: true,
minecraft: true,
username: true,
},
}),
this.prisma.user.count({ where: combinedFilter }),
]);

return {
data: users,
meta: {
page: pagination.page,
perPage: pagination.limit,
totalItems: count,
totalPages: Math.ceil(count / pagination.limit),
},
};
}
}