Skip to content
Merged
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
3 changes: 2 additions & 1 deletion src/entities/dto/chirpstack/gateway-contents.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiHideProperty, ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { Transform, Type } from "class-transformer";
import {
IsEmail,
IsEnum,
Expand Down Expand Up @@ -36,6 +36,7 @@ export class GatewayContentsDto {
@IsString()
@IsHexadecimal()
@Length(16, 16)
@Transform(({ value }) => value.toLowerCase())
gatewayId: string;

@ApiProperty({ required: false })
Expand Down
3 changes: 2 additions & 1 deletion src/entities/dto/chirpstack/update-gateway.dto.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ApiHideProperty, ApiProperty, OmitType } from "@nestjs/swagger";
import { ValidateNested } from "class-validator";
import { GatewayContentsDto } from "./gateway-contents.dto";
import { Type } from "class-transformer";
import { Transform, Type } from "class-transformer";

export class UpdateGatewayContentsDto extends OmitType(GatewayContentsDto, ["gatewayId"]) {
@ApiHideProperty()
@Transform(({ value }) => value.toLowerCase())
gatewayId: string;

@ApiHideProperty()
Expand Down
1 change: 1 addition & 0 deletions src/entities/enum/error-codes.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ export enum ErrorCodes {
UserAlreadyInPermission = "MESSAGE.USER-ALREADY-IN-PERMISSION",
CouldntGetApplications = "MESSAGE.COULD-NOT-GET-CS-APPLICATIONS",
DifferentServiceProfile = "MESSAGE.DIFFERENT-CREATION-SERVICE-PROFILE",
OrganizationCannotBeDeletedHasGateways = "MESSAGE.ORGANIZATION-GATEWAYS-EXISTS",
}
13 changes: 9 additions & 4 deletions src/entities/gateway.entity.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { DbBaseEntity } from "@entities/base.entity";
import { Column, Entity, ManyToOne } from "typeorm";
import { Organization } from "@entities/organization.entity";
import { Point } from "geojson";
import { IsEmail, IsPhoneNumber, Length } from "class-validator";
import { GatewayPlacement, GatewayStatus } from "@enum/gateway.enum";
import { Length } from "class-validator";
import { Point } from "geojson";
import { Column, Entity, ManyToOne } from "typeorm";

@Entity("gateway")
export class Gateway extends DbBaseEntity {
Expand All @@ -13,7 +13,12 @@ export class Gateway extends DbBaseEntity {
@Column({ nullable: true })
description?: string;

@Column()
@Column({
transformer: {
to: (value: string) => value.toLowerCase(),
from: (value: string) => value.toLowerCase(),
},
})
@Length(16, 16, { message: "Must be 16 characters" })
gatewayId: string;

Expand Down
33 changes: 27 additions & 6 deletions src/services/chirpstack/chirpstack-gateway.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DeleteGatewayRequest,
GetGatewayMetricsRequest,
GetGatewayMetricsResponse,
GetGatewayRequest,
GetGatewayResponse,
ListGatewaysRequest,
ListGatewaysResponse,
Expand Down Expand Up @@ -84,6 +85,18 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ
});

req.setGateway(gatewayChirpstack);

const getGatewayRequest = new GetGatewayRequest();
getGatewayRequest.setGatewayId(gateway.gatewayId);
const existingGateway = await this.get<GetGatewayResponse>("gateways", this.gatewayClient, getGatewayRequest).catch(
() => undefined
);

if (existingGateway)
throw new BadRequestException({
data: { message: "object already exists" },
});

try {
await this.post("gateways", this.gatewayClient, req);
await this.gatewayRepository.save(gateway);
Expand All @@ -99,7 +112,7 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ

async mapToChirpstackGateway(dto: CreateGatewayDto | UpdateGatewayDto, location: Location, gatewayId?: string) {
const gateway = new ChirpstackGateway();
gateway.setGatewayId(gatewayId ? gatewayId : dto.gateway.gatewayId);
gateway.setGatewayId(gatewayId ? gatewayId.toLowerCase() : dto.gateway.gatewayId);
gateway.setDescription(dto.gateway.description);
gateway.setName(dto.gateway.name);
gateway.setLocation(location);
Expand Down Expand Up @@ -186,6 +199,7 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ
if (gatewayId?.length != 16) {
throw new BadRequestException("Invalid gateway id");
}
gatewayId = gatewayId.toLowerCase();
try {
const result = new SingleGatewayResponseDto();
const gateway = await this.gatewayRepository.findOne({
Expand Down Expand Up @@ -216,7 +230,7 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ
const from_time_timestamp: Timestamp = dateToTimestamp(from);

const request = new GetGatewayMetricsRequest();
request.setGatewayId(gatewayId);
request.setGatewayId(gatewayId.toLowerCase());
request.setStart(from_time_timestamp);
request.setEnd(to_time);
request.setAggregation(Aggregation.DAY);
Expand Down Expand Up @@ -293,6 +307,7 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ
dto: UpdateGatewayDto,
req: AuthenticatedRequest
): Promise<ChirpstackResponseStatus> {
gatewayId = gatewayId.toLowerCase();
dto.gateway = await this.updateDtoContents(dto.gateway);
dto.gateway.tags = await this.ensureOrganizationIdIsSet(gatewayId, dto, req);
dto.gateway.tags = this.updateUpdatedByTag(dto, +req.user.userId);
Expand All @@ -312,8 +327,10 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ

request.setGateway(gatewayCs);
try {
await this.put("gateways", this.gatewayClient, request);
await this.gatewayRepository.update({ gatewayId }, gateway);
await this.put("gateways", this.gatewayClient, request).catch(
async () => await this.post("gateways", this.gatewayClient, request)
);
return { success: true };
} catch (e) {
this.logger.error(`Error from Chirpstack: '${JSON.stringify(dto)}', got response: ${JSON.stringify(e)}`);
Expand Down Expand Up @@ -342,15 +359,18 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ
updatedAt: Date,
lastSeenAt: Date | undefined
) {
await this.gatewayRepository.update({ gatewayId }, { rxPacketsReceived, txPacketsEmitted, lastSeenAt, updatedAt });
await this.gatewayRepository.update(
{ gatewayId: gatewayId.toLowerCase() },
{ rxPacketsReceived, txPacketsEmitted, lastSeenAt, updatedAt }
);
}

async ensureOrganizationIdIsSet(
gatewayId: string,
dto: UpdateGatewayDto,
req: AuthenticatedRequest
): Promise<{ [id: string]: string }> {
const existing = await this.getOne(gatewayId);
const existing = await this.getOne(gatewayId.toLowerCase());
const tags = dto.gateway.tags;
tags[this.ORG_ID_KEY] = `${existing.gateway.organizationId}`;
// TODO: Interpolated string will never be null?
Expand All @@ -362,10 +382,11 @@ export class ChirpstackGatewayService extends GenericChirpstackConfigurationServ

async deleteGateway(gatewayId: string): Promise<ChirpstackResponseStatus> {
const req = new DeleteGatewayRequest();
gatewayId = gatewayId.toLowerCase();
req.setGatewayId(gatewayId);
try {
await this.delete("gateways", this.gatewayClient, req);
await this.gatewayRepository.delete({ gatewayId });
await this.delete("gateways", this.gatewayClient, req);
return {
success: true,
};
Expand Down
7 changes: 6 additions & 1 deletion src/services/user-management/organization.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,12 @@ export class OrganizationService {
}

async delete(id: number): Promise<DeleteResponseDto> {
const res = await this.organizationRepository.delete(id);
const dbOrganization = await this.organizationRepository.findOne({ where: { id }, relations: { gateways: true } });
if (dbOrganization?.gateways?.length !== 0) {
throw new BadRequestException(ErrorCodes.OrganizationCannotBeDeletedHasGateways);
}

const res = await this.organizationRepository.delete(dbOrganization.id);
if (res.affected == 0) {
throw new NotFoundException();
}
Expand Down