add log aggregation with HyperDX
This commit is contained in:
parent
0951282f11
commit
62e663d053
File diff suppressed because it is too large
Load Diff
|
|
@ -25,6 +25,7 @@
|
||||||
"@apollo/client": "^3.13.8",
|
"@apollo/client": "^3.13.8",
|
||||||
"@azure/msal-angular": "^3.0.22",
|
"@azure/msal-angular": "^3.0.22",
|
||||||
"@azure/msal-browser": "^3.19.1",
|
"@azure/msal-browser": "^3.19.1",
|
||||||
|
"@hyperdx/browser": "^0.22.0",
|
||||||
"@ng-icons/core": "^31.4.0",
|
"@ng-icons/core": "^31.4.0",
|
||||||
"@ng-icons/lucide": "^31.4.0",
|
"@ng-icons/lucide": "^31.4.0",
|
||||||
"@ngneat/dialog": "^5.1.1",
|
"@ngneat/dialog": "^5.1.1",
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,21 @@ import { Component } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { AppShellComponent } from './core/components/app-shell/app-shell.component';
|
import { AppShellComponent } from './core/components/app-shell/app-shell.component';
|
||||||
import { MsalService } from '@azure/msal-angular';
|
import { MsalService } from '@azure/msal-angular';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [AppShellComponent, RouterOutlet],
|
imports: [AppShellComponent, RouterOutlet],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss'
|
styleUrl: './app.component.scss',
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'dashboard';
|
title = 'dashboard';
|
||||||
|
|
||||||
constructor(private readonly authService: MsalService) {
|
constructor(private readonly authService: MsalService) {
|
||||||
this.authService.handleRedirectObservable().subscribe();
|
this.authService
|
||||||
|
.handleRedirectObservable()
|
||||||
|
.pipe(takeUntilDestroyed())
|
||||||
|
.subscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import { registerInstrumentations } from '@opentelemetry/instrumentation';
|
|
||||||
import {
|
|
||||||
WebTracerProvider,
|
|
||||||
ConsoleSpanExporter,
|
|
||||||
SimpleSpanProcessor,
|
|
||||||
BatchSpanProcessor,
|
|
||||||
ParentBasedSampler,
|
|
||||||
TraceIdRatioBasedSampler,
|
|
||||||
} from '@opentelemetry/sdk-trace-web';
|
|
||||||
import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';
|
|
||||||
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
||||||
import { Resource } from '@opentelemetry/resources';
|
|
||||||
import { B3Propagator } from '@opentelemetry/propagator-b3';
|
|
||||||
import {
|
|
||||||
ATTR_SERVICE_NAME,
|
|
||||||
ATTR_SERVICE_VERSION,
|
|
||||||
} from '@opentelemetry/semantic-conventions';
|
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
|
|
||||||
const resource = Resource.default().merge(
|
|
||||||
new Resource({
|
|
||||||
[ATTR_SERVICE_NAME]: environment.production ? 'avicenna.dashboard.prod' : 'avicenna.dashboard',
|
|
||||||
// [ATTR_SERVICE_NAMESPACE]: 'avicenna',
|
|
||||||
[ATTR_SERVICE_VERSION]: '1.0',
|
|
||||||
environment: environment.production ? 'production' : 'staging'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const provider = new WebTracerProvider({
|
|
||||||
resource,
|
|
||||||
sampler: new ParentBasedSampler({
|
|
||||||
root: new TraceIdRatioBasedSampler(0.1),
|
|
||||||
}),
|
|
||||||
spanProcessors: [
|
|
||||||
new SimpleSpanProcessor(new ConsoleSpanExporter()),
|
|
||||||
new BatchSpanProcessor(
|
|
||||||
new OTLPTraceExporter({
|
|
||||||
url: 'https://collector.genius.ceo/v1/traces',
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
maxQueueSize: 100,
|
|
||||||
maxExportBatchSize: 10,
|
|
||||||
scheduledDelayMillis: 500,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
provider.register({
|
|
||||||
propagator: new B3Propagator(),
|
|
||||||
});
|
|
||||||
|
|
||||||
registerInstrumentations({
|
|
||||||
instrumentations: [
|
|
||||||
getWebAutoInstrumentations({
|
|
||||||
'@opentelemetry/instrumentation-document-load': {},
|
|
||||||
'@opentelemetry/instrumentation-user-interaction': {},
|
|
||||||
'@opentelemetry/instrumentation-fetch': {
|
|
||||||
propagateTraceHeaderCorsUrls: /.+/,
|
|
||||||
},
|
|
||||||
'@opentelemetry/instrumentation-xml-http-request': {
|
|
||||||
propagateTraceHeaderCorsUrls: /.+/,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
@ -115,12 +115,8 @@ export class DayviewComponent {
|
||||||
this.selectedDate.value ?? new Date(),
|
this.selectedDate.value ?? new Date(),
|
||||||
days
|
days
|
||||||
);
|
);
|
||||||
if (isSunday(nextSelectedDate)) {
|
|
||||||
this.selectedDate.setValue(addDays(nextSelectedDate, days));
|
|
||||||
} else {
|
|
||||||
this.selectedDate.setValue(nextSelectedDate);
|
this.selectedDate.setValue(nextSelectedDate);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
changeSelectedType(event: MatChipListboxChange) {
|
changeSelectedType(event: MatChipListboxChange) {
|
||||||
this.selectedMetric$.next(event.value);
|
this.selectedMetric$.next(event.value);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,18 @@
|
||||||
|
|
||||||
import '@angular/common/locales/global/de';
|
import '@angular/common/locales/global/de';
|
||||||
import { bootstrapApplication } from '@angular/platform-browser';
|
import { bootstrapApplication } from '@angular/platform-browser';
|
||||||
|
import HyperDX from '@hyperdx/browser';
|
||||||
import { AppComponent } from './app/app.component';
|
import { AppComponent } from './app/app.component';
|
||||||
import { appConfig } from './app/app.config';
|
import { appConfig } from './app/app.config';
|
||||||
import './app/core/telemetry/instrument';
|
|
||||||
|
HyperDX.init({
|
||||||
|
url: 'https://otlp.avicenna.hamburg',
|
||||||
|
apiKey: '71addf09-bcd2-4e83-8158-97458402137b',
|
||||||
|
service: 'avicenna-dashboard',
|
||||||
|
tracePropagationTargets: [/api/i, /graphql/i],
|
||||||
|
consoleCapture: true,
|
||||||
|
advancedNetworkCapture: true
|
||||||
|
});
|
||||||
|
|
||||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ WORKDIR /app
|
||||||
|
|
||||||
COPY --from=builder /app/builder/node_modules ./node_modules
|
COPY --from=builder /app/builder/node_modules ./node_modules
|
||||||
COPY --from=builder /app/builder/prisma ./prisma
|
COPY --from=builder /app/builder/prisma ./prisma
|
||||||
COPY --from=builder /app/builder/dist .
|
COPY --from=builder /app/builder/dist ./dist
|
||||||
COPY --from=builder /app/builder/docker-entrypoint.sh .
|
COPY --from=builder /app/builder/docker-entrypoint.sh .
|
||||||
|
|
||||||
ENTRYPOINT [ "./docker-entrypoint.sh" ]
|
ENTRYPOINT [ "./docker-entrypoint.sh" ]
|
||||||
|
|
||||||
CMD ["node", "./src/main.js" ]
|
CMD ["node", "./dist/src/main.js" ]
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -26,6 +26,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/server": "^4.12.0",
|
"@apollo/server": "^4.12.0",
|
||||||
"@faker-js/faker": "^9.6.0",
|
"@faker-js/faker": "^9.6.0",
|
||||||
|
"@hyperdx/node-logger": "^0.4.0",
|
||||||
|
"@hyperdx/node-opentelemetry": "^0.10.2",
|
||||||
"@nestjs-modules/mailer": "^2.0.2",
|
"@nestjs-modules/mailer": "^2.0.2",
|
||||||
"@nestjs/apollo": "^13.0.4",
|
"@nestjs/apollo": "^13.0.4",
|
||||||
"@nestjs/axios": "^4.0.0",
|
"@nestjs/axios": "^4.0.0",
|
||||||
|
|
@ -40,6 +42,7 @@
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@opentelemetry/auto-instrumentations-node": "^0.60.0",
|
"@opentelemetry/auto-instrumentations-node": "^0.60.0",
|
||||||
"@opentelemetry/exporter-trace-otlp-http": "^0.202.0",
|
"@opentelemetry/exporter-trace-otlp-http": "^0.202.0",
|
||||||
|
"@opentelemetry/instrumentation-runtime-node": "^0.22.0",
|
||||||
"@opentelemetry/sdk-node": "^0.202.0",
|
"@opentelemetry/sdk-node": "^0.202.0",
|
||||||
"@prisma/client": "^6.6.0",
|
"@prisma/client": "^6.6.0",
|
||||||
"bullmq": "^5.56.0",
|
"bullmq": "^5.56.0",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Controller, Get, Logger, Query } from '@nestjs/common';
|
import { Controller, Get, Logger, Query } from '@nestjs/common';
|
||||||
|
import { endOfDay, startOfDay } from 'date-fns';
|
||||||
import { PrismaService } from './core/database/prisma.service';
|
import { PrismaService } from './core/database/prisma.service';
|
||||||
import { AnomaliesService } from './modules/feat-anomalies/anomalies.service';
|
import { AnomaliesService } from './modules/feat-anomalies/anomalies.service';
|
||||||
import { CalculationService } from './modules/feat-calculation/calculation.service';
|
import { CalculationService } from './modules/feat-calculation/calculation.service';
|
||||||
|
|
@ -7,9 +8,8 @@ import { TicketService } from './modules/feat-tickets/tickets.service';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AppController {
|
export class AppController {
|
||||||
private logger = new Logger(AppController.name);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private readonly logger: Logger,
|
||||||
private readonly importer: ImporterService,
|
private readonly importer: ImporterService,
|
||||||
private readonly anomaliesService: AnomaliesService,
|
private readonly anomaliesService: AnomaliesService,
|
||||||
private readonly ticketService: TicketService,
|
private readonly ticketService: TicketService,
|
||||||
|
|
@ -30,6 +30,7 @@ export class AppController {
|
||||||
start: new Date(from),
|
start: new Date(from),
|
||||||
end: new Date(to),
|
end: new Date(to),
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.importer.runImportForRange(interval);
|
return this.importer.runImportForRange(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,8 +42,8 @@ export class AppController {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const interval = {
|
const interval = {
|
||||||
start: new Date(from),
|
start: startOfDay(new Date(from)),
|
||||||
end: new Date(to),
|
end: endOfDay(new Date(to)),
|
||||||
};
|
};
|
||||||
return this.ticketService.createTickets(interval);
|
return this.ticketService.createTickets(interval);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ type AttendanceRegistration {
|
||||||
dateTime: DateTime!
|
dateTime: DateTime!
|
||||||
employeeId: Int!
|
employeeId: Int!
|
||||||
event: String!
|
event: String!
|
||||||
personnelNumber: String!
|
personnelNumber: String
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RequestLoggerMiddleware implements NestMiddleware {
|
||||||
|
private logger = new Logger('HTTP');
|
||||||
|
|
||||||
|
use(request: Request, response: Response, next: NextFunction): void {
|
||||||
|
const { ip, method, originalUrl } = request;
|
||||||
|
const userAgent = request.get('user-agent') || '';
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
response.on('finish', () => {
|
||||||
|
const { statusCode } = response;
|
||||||
|
const contentLength = response.get('content-length');
|
||||||
|
const duration = Date.now() - start;
|
||||||
|
|
||||||
|
this.logger.log(
|
||||||
|
`${method} ${originalUrl} ${statusCode} ${contentLength} - ${userAgent} ${ip} +${duration}ms`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import * as HyperDX from '@hyperdx/node-opentelemetry';
|
||||||
|
import { RuntimeNodeInstrumentation } from '@opentelemetry/instrumentation-runtime-node';
|
||||||
|
|
||||||
|
|
||||||
|
HyperDX.init({
|
||||||
|
apiKey: '71addf09-bcd2-4e83-8158-97458402137b',
|
||||||
|
service: 'avicenna-data-connector',
|
||||||
|
|
||||||
|
instrumentations: {
|
||||||
|
'@opentelemetry/instrumentation-http': { enabled: true },
|
||||||
|
'@opentelemetry/instrumentation-express': { enabled: true },
|
||||||
|
'@opentelemetry/instrumentation-nestjs-core': { enabled: true },
|
||||||
|
'@opentelemetry/instrumentation-graphql': { enabled: true },
|
||||||
|
'@opentelemetry/instrumentation-pg': { enabled: true },
|
||||||
|
},
|
||||||
|
additionalInstrumentations: [
|
||||||
|
//@ts-ignore
|
||||||
|
new RuntimeNodeInstrumentation({
|
||||||
|
monitoringPrecision: 5000,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
@ -1,16 +1,23 @@
|
||||||
// const tracer = require('./tracer');
|
// const tracer = require('./tracer');
|
||||||
import tracer from './tracer'
|
// import tracer from './tracer'
|
||||||
|
import { HyperDXNestLoggerModule } from '@hyperdx/node-logger';
|
||||||
|
import * as HyperDX from '@hyperdx/node-opentelemetry';
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import { ConsoleLogger } from '@nestjs/common';
|
import './instrumentation';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
await tracer.start();
|
|
||||||
|
|
||||||
|
const logger = HyperDXNestLoggerModule.createLogger({
|
||||||
|
apiKey: '71addf09-bcd2-4e83-8158-97458402137b',
|
||||||
|
service: 'avicenna-data-connector',
|
||||||
|
maxLevel: 'info',
|
||||||
|
});
|
||||||
const app = await NestFactory.create(AppModule, {
|
const app = await NestFactory.create(AppModule, {
|
||||||
logger: new ConsoleLogger({ json: true }),
|
logger: logger,
|
||||||
});
|
});
|
||||||
app.enableCors();
|
app.enableCors();
|
||||||
|
HyperDX.setupExpressErrorHandler(app);
|
||||||
|
|
||||||
await app.listen(3000);
|
await app.listen(3000);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export class PlannedTimeObjectType implements PlannedTime {
|
||||||
pause: number;
|
pause: number;
|
||||||
@Field(() => Int)
|
@Field(() => Int)
|
||||||
duration: number;
|
duration: number;
|
||||||
@Field({nullable: true})
|
@Field({ nullable: true })
|
||||||
note: string;
|
note: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ export class EmployeeObjectType implements Employee {
|
||||||
export class AttendanceRegistrationObjectType
|
export class AttendanceRegistrationObjectType
|
||||||
implements AttendanceRegistration
|
implements AttendanceRegistration
|
||||||
{
|
{
|
||||||
@Field()
|
@Field({ nullable: true })
|
||||||
personnelNumber: string;
|
personnelNumber: string;
|
||||||
@Field(() => Int)
|
@Field(() => Int)
|
||||||
attendanceRegistrationId: number;
|
attendanceRegistrationId: number;
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ export class EmployeesLoggedInDetector extends AnomalyDetector {
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
private validateEmployee(employee: Employee | undefined) {
|
private validateEmployee(employee: Employee | undefined): employee is Employee {
|
||||||
if (!employee) {
|
if (!employee) {
|
||||||
this.logger.warn(`Employee does not exist`);
|
this.logger.warn(`Employee does not exist`);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -14,68 +14,6 @@ type TourEb = ExpressionBuilder<DB, 'Tour'>;
|
||||||
const PAUSE_PATIENT_ID = 'b7424477-a4b2-42f7-b7e7-a4bef24f6979';
|
const PAUSE_PATIENT_ID = 'b7424477-a4b2-42f7-b7e7-a4bef24f6979';
|
||||||
const FEIERABEND_PATIENT_ID = '3554e1a9-9c07-45e5-b53c-692a77e4fa9a';
|
const FEIERABEND_PATIENT_ID = '3554e1a9-9c07-45e5-b53c-692a77e4fa9a';
|
||||||
|
|
||||||
function onlyRealTours(): Prisma.TourWhereInput[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
OR: [
|
|
||||||
{
|
|
||||||
carName: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
AND: [
|
|
||||||
{
|
|
||||||
carName: {
|
|
||||||
not: 'Storno',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
carName: {
|
|
||||||
not: 'Löschen',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
carName: {
|
|
||||||
not: 'Abgabe',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
check: {
|
|
||||||
not: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deletedAt: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
OR: [
|
|
||||||
{
|
|
||||||
patientId: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
AND: [
|
|
||||||
{
|
|
||||||
patientId: {
|
|
||||||
// Pause
|
|
||||||
not: PAUSE_PATIENT_ID,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
patientId: {
|
|
||||||
// Feierabend
|
|
||||||
not: FEIERABEND_PATIENT_ID,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function onlyRealToursKysely(eb: TourEb) {
|
function onlyRealToursKysely(eb: TourEb) {
|
||||||
return eb.and([
|
return eb.and([
|
||||||
eb.or([
|
eb.or([
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
import { BullModule } from '@nestjs/bullmq';
|
import { BullModule } from '@nestjs/bullmq';
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { MailModule } from 'src/core/mail/mail.module';
|
||||||
|
import { MAILING_QUEUE } from 'src/core/mail/mail.tokens';
|
||||||
import { TOURS_QUEUE } from 'src/modules/feat-business-objects/entities/tour/tour.tokens';
|
import { TOURS_QUEUE } from 'src/modules/feat-business-objects/entities/tour/tour.tokens';
|
||||||
import { FeatBusinessObjectsModule } from 'src/modules/feat-business-objects/feat-business-objects.module';
|
import { FeatBusinessObjectsModule } from 'src/modules/feat-business-objects/feat-business-objects.module';
|
||||||
import { DataAccessExtractorsModule } from '../feat-extractors/data-access-extractors.module';
|
import { DataAccessExtractorsModule } from '../feat-extractors/data-access-extractors.module';
|
||||||
import { DataAccessLoadersModule } from '../feat-loaders/data-access-loaders.module';
|
import { DataAccessLoadersModule } from '../feat-loaders/data-access-loaders.module';
|
||||||
import { DataAccessTransformersModule } from '../feat-transformers/data-access-transformers.module';
|
import { DataAccessTransformersModule } from '../feat-transformers/data-access-transformers.module';
|
||||||
import { ImporterService } from './importer.service';
|
import { ImporterService } from './importer.service';
|
||||||
|
import { MailConsumer } from './processors/mail.consumer';
|
||||||
import { SchichtplanungConsumer } from './processors/schichtplanung.consumer';
|
import { SchichtplanungConsumer } from './processors/schichtplanung.consumer';
|
||||||
|
import { TourFileUploadConsumer } from './processors/tour-file-upload.consumer';
|
||||||
import { ToursConsumer } from './processors/tours.consumer';
|
import { ToursConsumer } from './processors/tours.consumer';
|
||||||
import { SchichtplanungSynchronizer } from './producer/schichtplanung';
|
import { SchichtplanungSynchronizer } from './producer/schichtplanung';
|
||||||
import { TourUpdateChecker } from './producer/tour-update';
|
import { TourUpdateChecker } from './producer/tour-update';
|
||||||
import { SCHICHTPLANUNG_QUEUE } from './tokens';
|
import { SCHICHTPLANUNG_QUEUE } from './tokens';
|
||||||
import { TourFileUploadConsumer } from './processors/tour-file-upload.consumer';
|
|
||||||
import { MAILING_QUEUE } from 'src/core/mail/mail.tokens';
|
|
||||||
import { MailConsumer } from './processors/mail.consumer';
|
|
||||||
import { MailModule } from 'src/core/mail/mail.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -34,7 +34,7 @@ import { MailModule } from 'src/core/mail/mail.module';
|
||||||
SchichtplanungSynchronizer,
|
SchichtplanungSynchronizer,
|
||||||
SchichtplanungConsumer,
|
SchichtplanungConsumer,
|
||||||
TourFileUploadConsumer,
|
TourFileUploadConsumer,
|
||||||
MailConsumer
|
MailConsumer,
|
||||||
],
|
],
|
||||||
exports: [ImporterService],
|
exports: [ImporterService],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { randomUUID } from 'crypto';
|
||||||
import { Interval } from 'date-fns';
|
import { Interval } from 'date-fns';
|
||||||
import { Observable, combineLatest, from, map, switchMap, tap } from 'rxjs';
|
import { Observable, combineLatest, from, map, switchMap, tap } from 'rxjs';
|
||||||
|
|
||||||
|
import { DyflexisUserAccessExtractorsService } from '../feat-extractors/dyflexis-user-access-extractors.service';
|
||||||
import { ExtractorsService } from '../feat-extractors/extractors.service';
|
import { ExtractorsService } from '../feat-extractors/extractors.service';
|
||||||
import { LoadersService } from '../feat-loaders/loaders.service';
|
import { LoadersService } from '../feat-loaders/loaders.service';
|
||||||
import { TransformersService } from '../feat-transformers/transformers.service';
|
import { TransformersService } from '../feat-transformers/transformers.service';
|
||||||
|
|
@ -11,9 +12,8 @@ import { ETLEventTypes } from './events/event-types.enum';
|
||||||
import { ExtractionDoneEvent } from './events/extraction-done.event';
|
import { ExtractionDoneEvent } from './events/extraction-done.event';
|
||||||
import { LoadingDoneEvent } from './events/loading-done.event';
|
import { LoadingDoneEvent } from './events/loading-done.event';
|
||||||
import { TransformationDoneEvent } from './events/transformation-done.event';
|
import { TransformationDoneEvent } from './events/transformation-done.event';
|
||||||
import { TourUpdateChecker } from './producer/tour-update';
|
|
||||||
import { SchichtplanungSynchronizer } from './producer/schichtplanung';
|
import { SchichtplanungSynchronizer } from './producer/schichtplanung';
|
||||||
import { DyflexisUserAccessExtractorsService } from '../feat-extractors/dyflexis-user-access-extractors.service';
|
import { TourUpdateChecker } from './producer/tour-update';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImporterService {
|
export class ImporterService {
|
||||||
|
|
@ -25,11 +25,11 @@ export class ImporterService {
|
||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
private readonly tourUpdateChecker: TourUpdateChecker,
|
private readonly tourUpdateChecker: TourUpdateChecker,
|
||||||
private readonly schichtplanungSynchronizer: SchichtplanungSynchronizer,
|
private readonly schichtplanungSynchronizer: SchichtplanungSynchronizer,
|
||||||
private readonly dyflexisUserAccessService: DyflexisUserAccessExtractorsService
|
private readonly dyflexisUserAccessService: DyflexisUserAccessExtractorsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
runDyflexisUserAccessTest() {
|
runDyflexisUserAccessTest() {
|
||||||
return this.dyflexisUserAccessService.loadData()
|
return this.dyflexisUserAccessService.loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
runTourUpdateCheck(interval: Interval) {
|
runTourUpdateCheck(interval: Interval) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
import { InjectQueue } from '@nestjs/bullmq';
|
import { InjectQueue } from '@nestjs/bullmq';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { Queue } from 'bullmq';
|
import { Queue } from 'bullmq';
|
||||||
import { addMinutes, endOfDay, getMonth, getYear, isSameDay, startOfDay } from 'date-fns';
|
|
||||||
import {
|
import {
|
||||||
lastValueFrom
|
addMinutes,
|
||||||
} from 'rxjs';
|
endOfDay,
|
||||||
|
getMonth,
|
||||||
|
getYear,
|
||||||
|
isSameDay,
|
||||||
|
startOfDay,
|
||||||
|
} from 'date-fns';
|
||||||
|
import { lastValueFrom } from 'rxjs';
|
||||||
import { EmployeeService } from 'src/modules/feat-business-objects/entities/employee/employee.service';
|
import { EmployeeService } from 'src/modules/feat-business-objects/entities/employee/employee.service';
|
||||||
import { DispoLiveApiExtractorsService } from '../../feat-extractors/dispo-live-api-extractors.service';
|
import { DispoLiveApiExtractorsService } from '../../feat-extractors/dispo-live-api-extractors.service';
|
||||||
import { DyflexisExtractorsService } from '../../feat-extractors/dyflexis-extractors.service';
|
import { DyflexisExtractorsService } from '../../feat-extractors/dyflexis-extractors.service';
|
||||||
|
|
@ -17,7 +22,7 @@ import { SCHICHTPLANUNG_QUEUE } from '../tokens';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SchichtplanungSynchronizer {
|
export class SchichtplanungSynchronizer {
|
||||||
private logger = new Logger(SchichtplanungSynchronizer.name)
|
private logger = new Logger(SchichtplanungSynchronizer.name);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly dlApiService: DispoLiveApiExtractorsService,
|
private readonly dlApiService: DispoLiveApiExtractorsService,
|
||||||
|
|
@ -42,7 +47,9 @@ export class SchichtplanungSynchronizer {
|
||||||
.filter(({ user_id }) => user_id != '0')
|
.filter(({ user_id }) => user_id != '0')
|
||||||
.filter(
|
.filter(
|
||||||
({ department_name }) =>
|
({ department_name }) =>
|
||||||
department_name !== 'Büro' && department_name !== 'Leitstelle',
|
department_name !== 'Büro' &&
|
||||||
|
department_name !== 'Leitstelle' &&
|
||||||
|
department_name !== 'Fortbildung',
|
||||||
)
|
)
|
||||||
.filter(({ start_date }) => isSameDay(new Date(start_date), day));
|
.filter(({ start_date }) => isSameDay(new Date(start_date), day));
|
||||||
|
|
||||||
|
|
@ -63,7 +70,9 @@ export class SchichtplanungSynchronizer {
|
||||||
plannedTime.user_id,
|
plannedTime.user_id,
|
||||||
);
|
);
|
||||||
if (!employee) {
|
if (!employee) {
|
||||||
this.logger.error(`couldn\'t find employee with id ${plannedTime.user_id}`);
|
this.logger.error(
|
||||||
|
`couldn\'t find employee with id ${plannedTime.user_id}`,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,7 +80,7 @@ export class SchichtplanungSynchronizer {
|
||||||
this.isCarNameForDepartment(plannedTime.department_name, kfzNr),
|
this.isCarNameForDepartment(plannedTime.department_name, kfzNr),
|
||||||
);
|
);
|
||||||
if (!car) {
|
if (!car) {
|
||||||
console.error(
|
this.logger.error(
|
||||||
`couldn\'t find car with department ${plannedTime.department_name}`,
|
`couldn\'t find car with department ${plannedTime.department_name}`,
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -83,13 +92,13 @@ export class SchichtplanungSynchronizer {
|
||||||
end: end_date.toISOString(),
|
end: end_date.toISOString(),
|
||||||
group: car._id,
|
group: car._id,
|
||||||
mitarbeiterId: employee.id,
|
mitarbeiterId: employee.id,
|
||||||
}
|
};
|
||||||
|
|
||||||
if(car.kfzNr.indexOf("KTW") > -1) {
|
if (car.kfzNr.indexOf('KTW') > -1) {
|
||||||
planning = {
|
planning = {
|
||||||
...planning,
|
...planning,
|
||||||
start: addMinutes(new Date(planning.start), 15).toISOString(),
|
start: addMinutes(new Date(planning.start), 15).toISOString(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
schichtplanung.push(planning);
|
schichtplanung.push(planning);
|
||||||
|
|
@ -126,7 +135,9 @@ export class SchichtplanungSynchronizer {
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!!plannedTime &&
|
!!plannedTime &&
|
||||||
(plannedTime?.start !== plan.start || plannedTime?.end !== plan.end || plannedTime?.group !== plan.group)
|
(plannedTime?.start !== plan.start ||
|
||||||
|
plannedTime?.end !== plan.end ||
|
||||||
|
plannedTime?.group !== plan.group)
|
||||||
) {
|
) {
|
||||||
toUpdate.push({ ...plan, _id: plannedTime._id });
|
toUpdate.push({ ...plan, _id: plannedTime._id });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ export class TicketLifecycleService {
|
||||||
|
|
||||||
private async validateCreatedTickets(): Promise<void> {
|
private async validateCreatedTickets(): Promise<void> {
|
||||||
const tickets = await this.ticketService.findAllCreated();
|
const tickets = await this.ticketService.findAllCreated();
|
||||||
this.logger.debug(`Found ${tickets.length} with state CREATED`);
|
this.logger.log(`validating ${tickets.length} created tickets`);
|
||||||
|
|
||||||
for (const ticket of tickets) {
|
for (const ticket of tickets) {
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue