diff --git a/src/app.module.ts b/src/app.module.ts index 0ab0eff..b4fbc3c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { ModbusReaderModule } from './modules/modbus-reader/modbus-reader.module'; import { AppConfigModule } from './config/config.module'; +import { MongoDatabaseProviderModule } from './config/database/mongo/provider/mongo-provider.module'; +import { ModbusReaderModule } from './modules/modbus-reader/modbus-reader.module'; @Module({ - imports: [ModbusReaderModule, AppConfigModule], + imports: [AppConfigModule, MongoDatabaseProviderModule, ModbusReaderModule], controllers: [AppController], providers: [AppService], }) diff --git a/src/config/configuration.ts b/src/config/configuration.ts index f173491..907496a 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -2,8 +2,7 @@ import { registerAs } from '@nestjs/config'; export default registerAs('app', () => ({ port: process.env.APP_PORT, - botToken: process.env.BOT_TOKEN, - publicKey: process.env.APP_PUBLIC_KEY, - clientId: process.env.CLIENT_ID, - serverId: process.env.SERVER_ID, + url: process.env.APP_URL, + name: process.env.APP_NAME, + env: process.env.NODE_ENV, })); diff --git a/src/config/database/mongo/config/config.module.ts b/src/config/database/mongo/config/config.module.ts new file mode 100644 index 0000000..79f924e --- /dev/null +++ b/src/config/database/mongo/config/config.module.ts @@ -0,0 +1,29 @@ +import * as Joi from 'joi'; +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import configuration from './configuration'; +import { MongoConfigService } from './config.service'; +/** + * Import and provide app configuration related classes. + * + * @module + */ +const ENV = process.env.NODE_ENV; +@Module({ + imports: [ + ConfigModule.forRoot({ + load: [configuration], + envFilePath: !ENV ? '.env' : `.env.${ENV}`, + validationSchema: Joi.object({ + MONGO_URI: Joi.string().default('localhost'), + MONGO_PORT: Joi.number().default(27017), + MONGO_USERNAME: Joi.string(), + MONGO_PASSWORD: Joi.string(), + MONGO_DATABASE: Joi.string().default(null), + }), + }), + ], + providers: [ConfigService, MongoConfigService], + exports: [ConfigService, MongoConfigService], +}) +export class MongoConfigModule {} diff --git a/src/config/database/mongo/config/config.service.ts b/src/config/database/mongo/config/config.service.ts new file mode 100644 index 0000000..4aaf177 --- /dev/null +++ b/src/config/database/mongo/config/config.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { + MongooseOptionsFactory, + MongooseModuleOptions, +} from '@nestjs/mongoose'; +/** + * Service dealing with app config based operations. + * + * @class + */ +@Injectable() +export class MongoConfigService implements MongooseOptionsFactory { + constructor(private configService: ConfigService) {} + + createMongooseOptions(): MongooseModuleOptions { + if (this.username && this.password) { + return { + uri: + `mongodb://${this.username}:${this.password}@${this.uri}` + + (this.database ? `/?authSource=${this.database}` : ''), + dbName: this.database, + }; + } else { + return { uri: this.uri }; + } + } + + get uri(): string { + return this.configService.get('mongo.uri'); + } + get username(): string { + return this.configService.get('mongo.username'); + } + get password(): string { + return this.configService.get('mongo.password'); + } + get database(): string { + return this.configService.get('mongo.database'); + } +} diff --git a/src/config/database/mongo/config/configuration.ts b/src/config/database/mongo/config/configuration.ts new file mode 100644 index 0000000..5a66724 --- /dev/null +++ b/src/config/database/mongo/config/configuration.ts @@ -0,0 +1,9 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs('mongo', () => ({ + uri: process.env.MONGO_URI, + port: process.env.MONGO_PORT, + username: process.env.MONGO_USERNAME, + password: process.env.MONGO_PASSWORD, + database: process.env.MONGO_DATABASE, +})); diff --git a/src/config/database/mongo/provider/mongo-provider.module.ts b/src/config/database/mongo/provider/mongo-provider.module.ts new file mode 100644 index 0000000..4710d53 --- /dev/null +++ b/src/config/database/mongo/provider/mongo-provider.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule, MongooseModuleAsyncOptions } from '@nestjs/mongoose'; +import { MongoConfigModule } from '../config/config.module'; +import { MongoConfigService } from '../config/config.service'; + +@Module({ + imports: [ + MongooseModule.forRootAsync({ + imports: [MongoConfigModule], + useExisting: MongoConfigService, + } as MongooseModuleAsyncOptions), + ], +}) +export class MongoDatabaseProviderModule {} diff --git a/src/dto/create-energy-entry.dto.ts b/src/dto/create-energy-entry.dto.ts new file mode 100644 index 0000000..1879872 --- /dev/null +++ b/src/dto/create-energy-entry.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { IsNotEmpty } from 'class-validator'; +import { UpdateEnergyEntryDto } from './update-energy-entry.dto'; + +export class CreateEnergyEntryDto extends OmitType(UpdateEnergyEntryDto, [ + '_id', + 'batteryLevel', +] as const) { + @ApiProperty({ + description: 'The battery level', + example: 100, + }) + @IsNotEmpty() + batteryLevel: number; +} diff --git a/src/dto/update-energy-entry.dto.ts b/src/dto/update-energy-entry.dto.ts new file mode 100644 index 0000000..4f2c3ea --- /dev/null +++ b/src/dto/update-energy-entry.dto.ts @@ -0,0 +1,40 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsOptional } from 'class-validator'; +import { ObjectId } from 'mongoose'; + +export class UpdateEnergyEntryDto { + @ApiProperty({ + description: 'The mongo id', + example: '2318931289123890', + }) + @IsNotEmpty() + _id: ObjectId; + + @ApiProperty({ + description: 'The battery level', + example: 100, + }) + @IsOptional() + batteryLevel: number; + + @ApiProperty({ + description: 'The battery level in percent', + example: '45%', + }) + @IsOptional() + batteryPercent: string; + + @ApiProperty({ + description: 'The battery voltage', + example: '5V', + }) + @IsOptional() + batteryVoltage: string; + + @ApiProperty({ + description: 'The battery status', + example: 'charging', + }) + @IsOptional() + batteryStatus: string; +} diff --git a/src/modules/energy-entry/energy-entry.controller.ts b/src/modules/energy-entry/energy-entry.controller.ts new file mode 100644 index 0000000..a648ed4 --- /dev/null +++ b/src/modules/energy-entry/energy-entry.controller.ts @@ -0,0 +1,80 @@ +import { + Body, + ClassSerializerInterceptor, + Controller, + Get, + Inject, + Param, + Post, + Put, + UseInterceptors, + UsePipes, + ValidationPipe, +} from '@nestjs/common'; +import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; +import { EnergyEntryService } from './energy-entry.service'; +import { CreateEnergyEntryDto } from '../../dto/create-energy-entry.dto'; +import { ReS } from '../../common/res.model'; +import { UpdateEnergyEntryDto } from '../../dto/update-energy-entry.dto'; +import { ObjectId } from 'mongoose'; + +@Controller('energy-data') +@ApiTags('energy-data') +export class EnergyEntryController { + constructor( + @Inject(EnergyEntryService) + private readonly modbusReaderService: EnergyEntryService, + ) {} + + @Post('/') + @UseInterceptors(ClassSerializerInterceptor) + @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) + @ApiOperation({ + summary: 'create enery entry', + description: 'create a new energy entry', + }) + async create(@Body() createDto: CreateEnergyEntryDto) { + return ReS.FromData(await this.modbusReaderService.create(createDto)); + } + + @Put('/') + @UseInterceptors(ClassSerializerInterceptor) + @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) + @ApiOperation({ + summary: 'update enery entry', + description: 'update a new energy entry', + }) + async update(@Body() updateDto: UpdateEnergyEntryDto) { + return ReS.FromData(await this.modbusReaderService.update(updateDto)); + } + + @Get('/') + @UseInterceptors(ClassSerializerInterceptor) + @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) + @ApiOperation({ + summary: 'get all data', + description: 'get all data from mongo', + }) + async getAll() { + return ReS.FromData(await this.modbusReaderService.getAll()); + } + + @Get('/:id') + @ApiParam({ + name: 'id', + description: 'The mongo id', + allowEmptyValue: false, + example: '222222222222222222222222', + required: true, + format: 'string', + }) + @UseInterceptors(ClassSerializerInterceptor) + @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) + @ApiOperation({ + summary: 'get data by id', + description: 'get data by id from mongo', + }) + async get(@Param('id') id: string | ObjectId) { + return ReS.FromData(await this.modbusReaderService.get(id)); + } +} diff --git a/src/modules/energy-entry/energy-entry.module.ts b/src/modules/energy-entry/energy-entry.module.ts new file mode 100644 index 0000000..f909ae7 --- /dev/null +++ b/src/modules/energy-entry/energy-entry.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { EnergyEntryService } from './energy-entry.service'; +import { EnergyEntryController } from './energy-entry.controller'; +import { MongooseModule } from '@nestjs/mongoose'; +import { + EnergyEntry, + EnergyEntrySchema, +} from '../../schemas/energy-entry.schema'; + +@Module({ + imports: [ + MongooseModule.forFeature([ + { + name: EnergyEntry.name, + schema: EnergyEntrySchema, + }, + ]), + ], // Add any imported modules here + controllers: [EnergyEntryController], // Add any controllers here + providers: [EnergyEntryService], + exports: [EnergyEntryService], // Add any providers (services) here +}) +export class EnergyEntryModule {} diff --git a/src/modules/energy-entry/energy-entry.service.ts b/src/modules/energy-entry/energy-entry.service.ts new file mode 100644 index 0000000..2a301f7 --- /dev/null +++ b/src/modules/energy-entry/energy-entry.service.ts @@ -0,0 +1,70 @@ +import { Injectable, UnprocessableEntityException } from '@nestjs/common'; +import { + EnergyEntry, + EnergyEntryDocument, +} from '../../schemas/energy-entry.schema'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model, ObjectId } from 'mongoose'; +import { CreateEnergyEntryDto } from '../../dto/create-energy-entry.dto'; +import { UpdateEnergyEntryDto } from '../../dto/update-energy-entry.dto'; + +@Injectable() +export class EnergyEntryService { + constructor( + @InjectModel(EnergyEntry.name) + private readonly energyEntryModel: Model, + ) {} + + async create(createDto: CreateEnergyEntryDto): Promise { + try { + return await this.energyEntryModel.create({ + batteryLevel: createDto.batteryLevel, + batteryPercent: createDto.batteryPercent, + batteryVoltage: createDto.batteryVoltage, + batteryStatus: createDto.batteryStatus, + occuredAt: new Date(), + }); + } catch (error) { + throw new UnprocessableEntityException(error); + } + } + + async update(updateDto: UpdateEnergyEntryDto): Promise { + try { + return await this.energyEntryModel.findOneAndUpdate( + { + _id: updateDto._id, + }, + { + batteryLevel: updateDto.batteryLevel, + batteryPercent: updateDto.batteryPercent, + batteryVoltage: updateDto.batteryVoltage, + batteryStatus: updateDto.batteryStatus, + }, + { + new: true, + }, + ); + } catch (error) { + throw new UnprocessableEntityException(error); + } + } + + async get(id: ObjectId | string): Promise { + try { + return await this.energyEntryModel.findOne({ + _id: id, + }); + } catch (error) { + throw new UnprocessableEntityException(error); + } + } + + async getAll(): Promise { + try { + return await this.energyEntryModel.find(); + } catch (error) { + throw new UnprocessableEntityException(error); + } + } +} diff --git a/src/modules/modbus-reader/modbus-reader.controller.ts b/src/modules/modbus-reader/modbus-reader.controller.ts deleted file mode 100644 index ba7b558..0000000 --- a/src/modules/modbus-reader/modbus-reader.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Inject } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { ModbusReaderService } from './modbus-reader.service'; - -@Controller('modbus-reader') -@ApiTags('modbus-reader') -export class ModbusReaderController { - constructor( - @Inject(ModbusReaderService) - private readonly modbusReaderService: ModbusReaderService, - ) {} -} diff --git a/src/modules/modbus-reader/modbus-reader.module.ts b/src/modules/modbus-reader/modbus-reader.module.ts index f6f17db..cef8a68 100644 --- a/src/modules/modbus-reader/modbus-reader.module.ts +++ b/src/modules/modbus-reader/modbus-reader.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; -import { ModbusReaderService } from './modbus-reader.service'; -import { ModbusReaderController } from './modbus-reader.controller'; +import { ModbusReaderService } from './modbus-reder.service'; +import { EnergyEntryModule } from '../energy-entry/energy-entry.module'; @Module({ - imports: [], // Add any imported modules here - controllers: [ModbusReaderController], // Add any controllers here + imports: [EnergyEntryModule], // Add any imported modules here + controllers: [], // Add any controllers here providers: [ModbusReaderService], exports: [ModbusReaderService], // Add any providers (services) here }) diff --git a/src/modules/modbus-reader/modbus-reader.service.ts b/src/modules/modbus-reader/modbus-reder.service.ts similarity index 100% rename from src/modules/modbus-reader/modbus-reader.service.ts rename to src/modules/modbus-reader/modbus-reder.service.ts diff --git a/src/schemas/energy-entry.schema.ts b/src/schemas/energy-entry.schema.ts new file mode 100644 index 0000000..09c34c0 --- /dev/null +++ b/src/schemas/energy-entry.schema.ts @@ -0,0 +1,27 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; + +@Schema() +export class EnergyEntry { + @Prop({ required: true }) + batteryLevel: number; + + @Prop({ required: false }) + batteryPercent: string; + + @Prop({ required: false }) + batteryVoltage: string; + + @Prop({ required: false }) + batteryStatus: string; + + @Prop({ required: true }) + occuredAt: Date; + + constructor(data) { + Object.assign(this, data); + } +} + +export type EnergyEntryDocument = EnergyEntry & Document; + +export const EnergyEntrySchema = SchemaFactory.createForClass(EnergyEntry);