Skip to content

Commit 4876351

Browse files
committed
Add lifecycle management module with DTOs, schema, service, and controller
1 parent 5825134 commit 4876351

File tree

6 files changed

+383
-1
lines changed

6 files changed

+383
-1
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
2+
import { IsEnum, IsNotEmpty, IsDateString, IsOptional } from 'class-validator';
3+
import { Types } from 'mongoose';
4+
import { IdentityLifecycle } from '~/management/identities/_enums/lifecycle.enum';
5+
6+
export class LifecycleDto {
7+
@ApiProperty({
8+
description: 'Unique identifier',
9+
type: String,
10+
})
11+
public _id: Types.ObjectId;
12+
13+
@ApiProperty({
14+
description: 'Reference to the identity',
15+
type: String,
16+
})
17+
@IsNotEmpty()
18+
public identityId: Types.ObjectId;
19+
20+
@ApiProperty({
21+
description: 'Lifecycle state',
22+
enum: IdentityLifecycle,
23+
})
24+
@IsEnum(IdentityLifecycle)
25+
@IsNotEmpty()
26+
public lifecycle: IdentityLifecycle;
27+
28+
@ApiProperty({
29+
description: 'Creation date',
30+
type: Date,
31+
})
32+
@IsDateString()
33+
public createdAt: Date;
34+
}
35+
36+
export class LifecycleCreateDto {
37+
@ApiProperty({
38+
description: 'Reference to the identity',
39+
type: String,
40+
})
41+
@IsNotEmpty()
42+
public identityId: Types.ObjectId;
43+
44+
@ApiProperty({
45+
description: 'Lifecycle state',
46+
enum: IdentityLifecycle,
47+
})
48+
@IsEnum(IdentityLifecycle)
49+
@IsNotEmpty()
50+
public lifecycle: IdentityLifecycle;
51+
}
52+
53+
export class LifecycleUpdateDto extends PartialType(LifecycleCreateDto) {
54+
@ApiPropertyOptional({
55+
description: 'Lifecycle state',
56+
enum: IdentityLifecycle,
57+
})
58+
@IsEnum(IdentityLifecycle)
59+
@IsOptional()
60+
public lifecycle?: IdentityLifecycle;
61+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
2+
import { Document, Types } from 'mongoose';
3+
import { AbstractSchema } from '~/_common/abstracts/schemas/abstract.schema';
4+
import { IdentityLifecycle } from '~/management/identities/_enums/lifecycle.enum';
5+
6+
export type LifecycleDocument = Lifecycle & Document;
7+
8+
@Schema({ versionKey: false, minimize: false })
9+
export class Lifecycle extends AbstractSchema {
10+
@Prop({ type: Types.ObjectId, ref: 'Identities', required: true })
11+
public identityId: Types.ObjectId;
12+
13+
@Prop({ type: Number, enum: IdentityLifecycle, required: true })
14+
public lifecycle: IdentityLifecycle;
15+
16+
@Prop({ type: Date, default: Date.now })
17+
public createdAt: Date;
18+
}
19+
20+
export const LifecycleSchema = SchemaFactory.createForClass(Lifecycle);
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import {
2+
Body,
3+
Controller,
4+
Delete,
5+
Get,
6+
HttpStatus,
7+
Param,
8+
Patch,
9+
Post,
10+
Res,
11+
} from '@nestjs/common';
12+
import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
13+
import { Response } from 'express';
14+
import { Types } from 'mongoose';
15+
import { AbstractController } from '~/_common/abstracts/abstract.controller';
16+
import { ApiCreateDecorator } from '~/_common/decorators/api-create.decorator';
17+
import { ApiPaginatedDecorator } from '~/_common/decorators/api-paginated.decorator';
18+
import { ApiReadResponseDecorator } from '~/_common/decorators/api-read-response.decorator';
19+
import { ApiUpdateDecorator } from '~/_common/decorators/api-update.decorator';
20+
import { ObjectIdValidationPipe } from '~/_common/pipes/object-id-validation.pipe';
21+
import { LifecycleCreateDto, LifecycleDto, LifecycleUpdateDto } from './_dto/lifecycle.dto';
22+
import { LifecycleService } from './lifecycle.service';
23+
24+
@ApiTags('management/lifecycle')
25+
@Controller('lifecycle')
26+
export class LifecycleController extends AbstractController {
27+
public constructor(
28+
protected readonly _service: LifecycleService,
29+
) {
30+
super();
31+
}
32+
33+
@ApiOperation({ summary: 'Create a new lifecycle record' })
34+
@ApiCreateDecorator(LifecycleCreateDto, LifecycleDto)
35+
@Post()
36+
public async create(
37+
@Body() dto: LifecycleCreateDto,
38+
@Res() response: Response,
39+
): Promise<void> {
40+
try {
41+
const result = await this._service.createLifecycle(dto);
42+
response.status(HttpStatus.CREATED).json(result);
43+
} catch (error) {
44+
this.handleError(error, response);
45+
}
46+
}
47+
48+
@ApiOperation({ summary: 'Get all lifecycle records' })
49+
@ApiPaginatedDecorator(LifecycleDto)
50+
@Get()
51+
public async findAll(
52+
@Res() response: Response,
53+
): Promise<void> {
54+
try {
55+
const result = await this._service.find();
56+
response.status(HttpStatus.OK).json(result);
57+
} catch (error) {
58+
this.handleError(error, response);
59+
}
60+
}
61+
62+
@ApiOperation({ summary: 'Get lifecycle record by ID' })
63+
@ApiParam({ name: 'id', description: 'Lifecycle ID' })
64+
@ApiReadResponseDecorator(LifecycleDto)
65+
@Get(':id')
66+
public async findOne(
67+
@Param('id', ObjectIdValidationPipe) id: Types.ObjectId,
68+
@Res() response: Response,
69+
): Promise<void> {
70+
try {
71+
const result = await this._service.findById(id);
72+
response.status(HttpStatus.OK).json(result);
73+
} catch (error) {
74+
this.handleError(error, response);
75+
}
76+
}
77+
78+
@ApiOperation({ summary: 'Update lifecycle record' })
79+
@ApiParam({ name: 'id', description: 'Lifecycle ID' })
80+
@ApiUpdateDecorator(LifecycleUpdateDto, LifecycleDto)
81+
@Patch(':id')
82+
public async update(
83+
@Param('id', ObjectIdValidationPipe) id: Types.ObjectId,
84+
@Body() dto: LifecycleUpdateDto,
85+
@Res() response: Response,
86+
): Promise<void> {
87+
try {
88+
const result = await this._service.updateLifecycle(id, dto);
89+
response.status(HttpStatus.OK).json(result);
90+
} catch (error) {
91+
this.handleError(error, response);
92+
}
93+
}
94+
95+
@ApiOperation({ summary: 'Delete lifecycle record' })
96+
@ApiParam({ name: 'id', description: 'Lifecycle ID' })
97+
@Delete(':id')
98+
public async remove(
99+
@Param('id', ObjectIdValidationPipe) id: Types.ObjectId,
100+
@Res() response: Response,
101+
): Promise<void> {
102+
try {
103+
await this._service.delete(id);
104+
response.status(HttpStatus.NO_CONTENT).send();
105+
} catch (error) {
106+
this.handleError(error, response);
107+
}
108+
}
109+
110+
@ApiOperation({ summary: 'Get lifecycle history for an identity' })
111+
@ApiParam({ name: 'identityId', description: 'Identity ID' })
112+
@Get('identity/:identityId')
113+
public async getLifecycleHistory(
114+
@Param('identityId', ObjectIdValidationPipe) identityId: Types.ObjectId,
115+
@Res() response: Response,
116+
): Promise<void> {
117+
try {
118+
const result = await this._service.getLifecycleHistory(identityId);
119+
response.status(HttpStatus.OK).json(result);
120+
} catch (error) {
121+
this.handleError(error, response);
122+
}
123+
}
124+
125+
@ApiOperation({ summary: 'Get lifecycle statistics' })
126+
@Get('stats')
127+
public async getStats(
128+
@Res() response: Response,
129+
): Promise<void> {
130+
try {
131+
const result = await this._service.getLifecycleStats();
132+
response.status(HttpStatus.OK).json(result);
133+
} catch (error) {
134+
this.handleError(error, response);
135+
}
136+
}
137+
138+
@ApiOperation({ summary: 'Get recent lifecycle changes' })
139+
@Get('recent')
140+
public async getRecentChanges(
141+
@Res() response: Response,
142+
): Promise<void> {
143+
try {
144+
const result = await this._service.getRecentChanges();
145+
response.status(HttpStatus.OK).json(result);
146+
} catch (error) {
147+
this.handleError(error, response);
148+
}
149+
}
150+
151+
private handleError(error: any, response: Response): void {
152+
console.error('Lifecycle Controller Error:', error);
153+
response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
154+
message: 'An error occurred while processing the request',
155+
error: error.message,
156+
});
157+
}
158+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Module } from '@nestjs/common';
2+
import { MongooseModule } from '@nestjs/mongoose';
3+
import { Lifecycle, LifecycleSchema } from './_schemas/lifecycle.schema';
4+
import { LifecycleController } from './lifecycle.controller';
5+
import { LifecycleService } from './lifecycle.service';
6+
7+
@Module({
8+
imports: [
9+
MongooseModule.forFeature([
10+
{
11+
name: Lifecycle.name,
12+
schema: LifecycleSchema,
13+
},
14+
]),
15+
],
16+
providers: [LifecycleService],
17+
controllers: [LifecycleController],
18+
exports: [LifecycleService],
19+
})
20+
export class LifecycleModule { }
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { InjectModel } from '@nestjs/mongoose';
3+
import { Model, Types } from 'mongoose';
4+
import { AbstractServiceSchema } from '~/_common/abstracts/abstract.service.schema';
5+
import { Lifecycle, LifecycleDocument } from './_schemas/lifecycle.schema';
6+
import { LifecycleCreateDto, LifecycleUpdateDto } from './_dto/lifecycle.dto';
7+
import { IdentityLifecycle } from '~/management/identities/_enums/lifecycle.enum';
8+
import { OnEvent } from '@nestjs/event-emitter';
9+
10+
@Injectable()
11+
export class LifecycleService extends AbstractServiceSchema {
12+
protected _model: Model<LifecycleDocument>;
13+
14+
public constructor(
15+
@InjectModel(Lifecycle.name) model: Model<LifecycleDocument>,
16+
) {
17+
super();
18+
this._model = model;
19+
}
20+
21+
@OnEvent('management.identities.service.beforeFindAndCount')
22+
public async handleOrderCreatedEvent(event: any): Promise<void> {
23+
console.log('Order created event received:', event);
24+
}
25+
26+
/**
27+
* Create a new lifecycle record
28+
*/
29+
public async createLifecycle(dto: LifecycleCreateDto): Promise<Lifecycle> {
30+
const lifecycleData = {
31+
identityId: dto.identityId,
32+
lifecycle: dto.lifecycle,
33+
createdAt: new Date(),
34+
};
35+
36+
const created = await this.create(lifecycleData);
37+
return created as unknown as Lifecycle;
38+
}
39+
40+
/**
41+
* Update a lifecycle record
42+
*/
43+
public async updateLifecycle(id: Types.ObjectId, dto: LifecycleUpdateDto): Promise<Lifecycle> {
44+
const updateData = {
45+
...dto,
46+
updatedAt: new Date(),
47+
};
48+
49+
const updated = await this.update(id, updateData);
50+
return updated.value as unknown as Lifecycle;
51+
}
52+
53+
/**
54+
* Get lifecycle history for a specific identity
55+
*/
56+
public async getLifecycleHistory(identityId: Types.ObjectId): Promise<Lifecycle[]> {
57+
const results = await this.find({ identityId }, null, { sort: { createdAt: -1 } });
58+
return results as unknown as Lifecycle[];
59+
}
60+
61+
/**
62+
* Get current lifecycle status for an identity
63+
*/
64+
public async getCurrentLifecycle(identityId: Types.ObjectId): Promise<Lifecycle | null> {
65+
try {
66+
const result = await this.findOne({ identityId }, null, { sort: { createdAt: -1 } });
67+
return result as unknown as Lifecycle;
68+
} catch (error) {
69+
return null;
70+
}
71+
}
72+
73+
/**
74+
* Add a new lifecycle event for an identity
75+
*/
76+
public async addLifecycleEvent(
77+
identityId: Types.ObjectId,
78+
lifecycle: IdentityLifecycle,
79+
): Promise<Lifecycle> {
80+
const lifecycleData = {
81+
identityId,
82+
lifecycle,
83+
createdAt: new Date(),
84+
};
85+
86+
const created = await this.create(lifecycleData);
87+
return created as unknown as Lifecycle;
88+
}
89+
90+
/**
91+
* Get lifecycle statistics
92+
*/
93+
public async getLifecycleStats(): Promise<any> {
94+
const stats = await this._model.aggregate([
95+
{
96+
$group: {
97+
_id: '$lifecycle',
98+
count: { $sum: 1 },
99+
},
100+
},
101+
{
102+
$sort: { _id: 1 },
103+
},
104+
]);
105+
106+
return stats;
107+
}
108+
109+
/**
110+
* Get recent lifecycle changes
111+
*/
112+
public async getRecentChanges(limit: number = 10): Promise<Lifecycle[]> {
113+
const results = await this._model
114+
.find()
115+
.sort({ createdAt: -1 })
116+
.limit(limit)
117+
.populate('identityId', 'inetOrgPerson.cn inetOrgPerson.mail')
118+
.exec();
119+
120+
return results as unknown as Lifecycle[];
121+
}
122+
}

src/management/management.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import { ManagementController } from './management.controller';
44
import { RouterModule } from '@nestjs/core';
55
import { IdentitiesModule } from './identities/identities.module';
66
import { PasswdModule } from './passwd/passwd.module';
7+
import { LifecycleModule } from './lifecycle/lifecycle.module';
78

89
@Module({
9-
imports: [IdentitiesModule, PasswdModule],
10+
imports: [IdentitiesModule, PasswdModule, LifecycleModule],
1011
providers: [ManagementService],
1112
controllers: [ManagementController],
1213
})

0 commit comments

Comments
 (0)