Skip to content

Commit

Permalink
IOT-1276: Implemented sorting for gateway table (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
fcv-iteratorIt authored Dec 18, 2023
1 parent d7c140b commit 783ec99
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 53 deletions.
54 changes: 27 additions & 27 deletions src/controllers/admin-controller/application.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,33 +85,6 @@ export class ApplicationController {
return await this.getApplicationsForNonGlobalAdmin(req, query);
}

private async getApplicationsForNonGlobalAdmin(req: AuthenticatedRequest, query: ListAllApplicationsDto) {
if (query?.organizationId) {
checkIfUserHasAccessToOrganization(req, query.organizationId, OrganizationAccessScope.ApplicationRead);
return await this.getApplicationsInOrganization(req, query);
}

const allFromOrg = req.user.permissions.getAllOrganizationsWithApplicationAdmin();
const allowedApplications = req.user.permissions.getAllApplicationsWithAtLeastRead();
const applications = await this.applicationService.findAndCountApplicationInWhitelistOrOrganization(
query,
allowedApplications,
query.organizationId ? [query.organizationId] : allFromOrg
);
return applications;
}

private async getApplicationsInOrganization(req: AuthenticatedRequest, query: ListAllApplicationsDto) {
// User admins have access to all applications in the organization
const allFromOrg = req.user.permissions.getAllOrganizationsWithUserAdmin();
if (allFromOrg.some(x => x === query?.organizationId)) {
return await this.applicationService.findAndCountWithPagination(query, [query.organizationId]);
}

const allowedApplications = req.user.permissions.getAllApplicationsWithAtLeastRead();
return await this.applicationService.findAndCountInList(query, allowedApplications, [query.organizationId]);
}

@Read()
@Get(":id")
@ApiOperation({ summary: "Find one Application by id" })
Expand Down Expand Up @@ -221,4 +194,31 @@ export class ApplicationController {
throw new NotFoundException(err);
}
}

private async getApplicationsForNonGlobalAdmin(req: AuthenticatedRequest, query: ListAllApplicationsDto) {
if (query?.organizationId) {
checkIfUserHasAccessToOrganization(req, query.organizationId, OrganizationAccessScope.ApplicationRead);
return await this.getApplicationsInOrganization(req, query);
}

const allFromOrg = req.user.permissions.getAllOrganizationsWithApplicationAdmin();
const allowedApplications = req.user.permissions.getAllApplicationsWithAtLeastRead();
const applications = await this.applicationService.findAndCountApplicationInWhitelistOrOrganization(
query,
allowedApplications,
query.organizationId ? [query.organizationId] : allFromOrg
);
return applications;
}

private async getApplicationsInOrganization(req: AuthenticatedRequest, query: ListAllApplicationsDto) {
// User admins have access to all applications in the organization
const allFromOrg = req.user.permissions.getAllOrganizationsWithUserAdmin();
if (allFromOrg.some(x => x === query?.organizationId)) {
return await this.applicationService.findAndCountWithPagination(query, [query.organizationId]);
}

const allowedApplications = req.user.permissions.getAllApplicationsWithAtLeastRead();
return await this.applicationService.findAndCountInList(query, allowedApplications, [query.organizationId]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { GatewayAdmin, Read } from "@auth/roles.decorator";
import { RolesGuard } from "@auth/roles.guard";
import { ChirpstackResponseStatus } from "@dto/chirpstack/chirpstack-response.dto";
import { CreateGatewayDto } from "@dto/chirpstack/create-gateway.dto";
import { ListAllGatewaysResponseDto } from "@dto/chirpstack/list-all-gateways.dto";
import { SingleGatewayResponseDto } from "@dto/chirpstack/single-gateway-response.dto";
import { UpdateGatewayDto } from "@dto/chirpstack/update-gateway.dto";
import { ErrorCodes } from "@enum/error-codes.enum";
Expand All @@ -27,9 +26,10 @@ import { checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@he
import { AuthenticatedRequest } from "@dto/internal/authenticated-request";
import { AuditLog } from "@services/audit-log.service";
import { ActionType } from "@entities/audit-log-entry";
import { ChirpstackGetAll } from "@dto/chirpstack/chirpstack-get-all.dto";
import { ComposeAuthGuard } from "@auth/compose-auth.guard";
import { ApiAuth } from "@auth/swagger-auth-decorator";
import { ListAllGatewaysDto } from "@dto/chirpstack/list-all-gateways.dto";
import { ListAllGatewaysResponseDto } from "@dto/chirpstack/list-all-gateways-response.dto";

@ApiTags("Chirpstack")
@Controller("chirpstack/gateway")
Expand Down Expand Up @@ -75,8 +75,8 @@ export class ChirpstackGatewayController {
@ApiProduces("application/json")
@ApiOperation({ summary: "List all Chirpstack gateways" })
@Read()
async getAll(@Query() query?: ChirpstackGetAll): Promise<ListAllGatewaysResponseDto> {
return await this.chirpstackGatewayService.getAll(query.organizationId);
async getAll(@Query() query?: ListAllGatewaysDto): Promise<ListAllGatewaysResponseDto> {
return await this.chirpstackGatewayService.getWithPaginationAndSorting(query, query.organizationId);
}

@Get(":gatewayId")
Expand Down
6 changes: 0 additions & 6 deletions src/entities/dto/chirpstack/chirpstack-get-all.dto.ts

This file was deleted.

11 changes: 11 additions & 0 deletions src/entities/dto/chirpstack/list-all-gateways-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ChirpstackGatewayResponseDto, GatewayResponseDto } from "./gateway-response.dto";

export class ListAllGatewaysResponseDto {
totalCount: number;
resultList: GatewayResponseDto[];
}

export class ListAllChirpstackGatewaysResponseDto {
totalCount: number;
resultList: ChirpstackGatewayResponseDto[];
}
31 changes: 23 additions & 8 deletions src/entities/dto/chirpstack/list-all-gateways.dto.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import { ChirpstackGatewayResponseDto, GatewayResponseDto } from "./gateway-response.dto";
import { ApiProperty, OmitType, PickType } from "@nestjs/swagger";
import { ChirpstackPaginatedListDto } from "./chirpstack-paginated-list.dto";
import { ListAllEntitiesDto } from "@dto/list-all-entities.dto";
import { IsSwaggerOptional } from "@helpers/optional-validator";
import { NullableStringToNumber, StringToNumber } from "@helpers/string-to-number-validator";
import { IsNumber, IsOptional } from "class-validator";
import { Transform } from "class-transformer";
import { DefaultLimit, DefaultOffset } from "@config/constants/pagination-constants";

export class ListAllGatewaysResponseDto {
totalCount: number;
resultList: GatewayResponseDto[];
}
export class ListAllGatewaysDto extends OmitType(ListAllEntitiesDto, ["limit", "offset"]) {
@IsSwaggerOptional({ description: "Filter to one organization" })
@StringToNumber()
organizationId?: number;

@ApiProperty({ type: Number, required: false })
@IsOptional()
@IsNumber()
@Transform(({ value }) => NullableStringToNumber(value))
limit? = DefaultLimit;

export class ListAllChirpstackGatewaysResponseDto {
totalCount: number;
resultList: ChirpstackGatewayResponseDto[];
@ApiProperty({ type: Number, required: false })
@IsOptional()
@IsNumber()
@Transform(({ value }) => NullableStringToNumber(value))
offset? = DefaultOffset;
}
3 changes: 2 additions & 1 deletion src/entities/dto/list-all-entities.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ export class ListAllEntitiesDto {
| "openDataDkEnabled"
| "deviceModel"
| "devices"
| "dataTargets";
| "dataTargets"
| "organizationName";
}
62 changes: 56 additions & 6 deletions src/services/chirpstack/chirpstack-gateway.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ import { ChirpstackErrorResponseDto } from "@dto/chirpstack/chirpstack-error-res
import { ChirpstackResponseStatus } from "@dto/chirpstack/chirpstack-response.dto";
import { CreateGatewayDto } from "@dto/chirpstack/create-gateway.dto";
import { GatewayStatsElementDto } from "@dto/chirpstack/gateway-stats.response.dto";
import {
ListAllChirpstackGatewaysResponseDto,
ListAllGatewaysResponseDto,
} from "@dto/chirpstack/list-all-gateways.dto";
import { SingleGatewayResponseDto } from "@dto/chirpstack/single-gateway-response.dto";
import { UpdateGatewayContentsDto, UpdateGatewayDto } from "@dto/chirpstack/update-gateway.dto";
import { ErrorCodes } from "@enum/error-codes.enum";
Expand All @@ -33,13 +29,19 @@ import {
GetGatewayMetricsResponse,
GetGatewayResponse,
ListGatewaysRequest,
UpdateGatewayRequest,
ListGatewaysResponse,
UpdateGatewayRequest,
} from "@chirpstack/chirpstack-api/api/gateway_pb";
import { Timestamp } from "google-protobuf/google/protobuf/timestamp_pb";
import { Aggregation, Location } from "@chirpstack/chirpstack-api/common/common_pb";
import { dateToTimestamp, timestampToDate } from "@helpers/date.helper";
import { ChirpstackGatewayResponseDto, GatewayResponseDto } from "@dto/chirpstack/gateway-response.dto";
import { ListAllEntitiesDto } from "@dto/list-all-entities.dto";
import {
ListAllChirpstackGatewaysResponseDto,
ListAllGatewaysResponseDto,
} from "@dto/chirpstack/list-all-gateways-response.dto";

@Injectable()
export class ChirpstackGatewayService extends GenericChirpstackConfigurationService {
constructor(
Expand Down Expand Up @@ -148,6 +150,32 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ
};
}

public async getWithPaginationAndSorting(
queryParams?: ListAllEntitiesDto,
organizationId?: number
): Promise<ListAllGatewaysResponseDto> {
const orderByColumn = this.getSortingForGateways(queryParams);
const direction = queryParams?.sort?.toUpperCase() === "DESC" ? "DESC" : "ASC";

let query = this.gatewayRepository
.createQueryBuilder("gateway")
.innerJoinAndSelect("gateway.organization", "organization")
.skip(queryParams?.offset ? +queryParams.offset : 0)
.take(queryParams.limit ? +queryParams.limit : 100)
.orderBy(orderByColumn, direction);

if (organizationId) {
query = query.where('"organizationId" = :organizationId', { organizationId });
}

const [gateways, count] = await query.getManyAndCount();

return {
resultList: gateways.map(this.mapGatewayToResponseDto),
totalCount: count,
};
}

async getOne(gatewayId: string): Promise<SingleGatewayResponseDto> {
if (gatewayId?.length != 16) {
throw new BadRequestException("Invalid gateway id");
Expand Down Expand Up @@ -294,9 +322,13 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ
gatewayId: string,
rxPacketsReceived: number,
txPacketsEmitted: number,
updatedAt: Date,
lastSeenAt: Date | undefined
) {
await this.gatewayRepository.update({ gatewayId }, { rxPacketsReceived, txPacketsEmitted, lastSeenAt });
await this.gatewayRepository.update(
{ gatewayId },
{ rxPacketsReceived, txPacketsEmitted, lastSeenAt, updatedAt }
);
}

async ensureOrganizationIdIsSet(
Expand Down Expand Up @@ -454,4 +486,22 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ
};
return responseList;
}

private getSortingForGateways(query: ListAllEntitiesDto) {
let orderBy = "gateway.id";

if (query.orderOn == null) {
return orderBy;
}

if (query.orderOn === "organizationName") {
orderBy = "organization.name";
} else if (query.orderOn === "status") {
orderBy = "gateway.lastSeenAt";
} else {
orderBy = `gateway.${query.orderOn}`;
}

return orderBy;
}
}
2 changes: 1 addition & 1 deletion src/services/chirpstack/gateway-boostrapper.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ListAllGatewaysResponseDto } from "@dto/chirpstack/list-all-gateways.dto";
import { GatewayStatusHistory } from "@entities/gateway-status-history.entity";
import { Inject, InternalServerErrorException, Logger, OnApplicationBootstrap } from "@nestjs/common";
import { ChirpstackGatewayService } from "./chirpstack-gateway.service";
import { GatewayStatusHistoryService } from "./gateway-status-history.service";
import { ListAllGatewaysResponseDto } from "@dto/chirpstack/list-all-gateways-response.dto";

/**
* Verify if any gateways exist on chirpstack and haven't been loaded into the database.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class LorawanDeviceDatabaseEnrichJob {
gateway.gatewayId,
stats.rxPacketsReceived,
stats.txPacketsEmitted,
gateway.updatedAt,
chirpstackGateway.lastSeenAt ? timestampToDate(chirpstackGateway.lastSeenAt) : undefined
);
} catch (err) {
Expand Down

0 comments on commit 783ec99

Please sign in to comment.