init data-connector

This commit is contained in:
Marcel Arndt
2025-04-10 17:32:06 +02:00
parent 52c1a14609
commit 46631db5b2
105 changed files with 8081 additions and 338 deletions
@@ -0,0 +1,9 @@
import { ObjectType, Field } from '@nestjs/graphql';
@ObjectType('Interval')
export class IntervalObjectType {
@Field()
start: Date;
@Field()
end: Date;
}
@@ -0,0 +1,57 @@
import { Type } from '@nestjs/common';
import { ArgsType, Field, Int, ObjectType } from '@nestjs/graphql';
@ArgsType()
export class PaginationArgs {
@Field(() => Int)
take = 10;
@Field(() => String, { nullable: true })
cursor?: string;
}
// interface IEdgeType<T> {
// cursor: string;
// node: T;
// }
export interface IPaginatedType<T> {
// edges: IEdgeType<T>[];
nodes: T[];
prevCursor: string;
endCursor: string;
totalCount: number;
hasNextPage: boolean;
}
export function Paginated<T>(classRef: Type<T>): Type<IPaginatedType<T>> {
// @ObjectType(`${classRef.name}Edge`)
// abstract class EdgeType {
// @Field(() => String)
// cursor: string;
// @Field(() => classRef)
// node: T;
// }
@ObjectType({ isAbstract: true })
abstract class PaginatedType implements IPaginatedType<T> {
// @Field(() => [EdgeType], { nullable: true })
// edges: EdgeType[];
@Field(() => [classRef])
nodes: T[];
@Field(() => String)
prevCursor: string;
@Field(() => String)
endCursor: string;
@Field(() => Int)
totalCount: number;
@Field()
hasNextPage: boolean;
}
return PaginatedType as Type<IPaginatedType<T>>;
}
@@ -0,0 +1,11 @@
export function encodeCursor(cursor: string | number): string {
return Buffer.from(`${cursor}`).toString('base64');
}
export function decodeCursor(cursor: string): string {
return Buffer.from(cursor, 'base64').toString('ascii');
}
export function decodeOffsetCursor(cursor: string): number {
return Number.parseInt(decodeCursor(cursor));
}
@@ -24,4 +24,19 @@ export class ConfigService extends NestConfigService {
dyflexisV2ApiKey(): string {
return this.get('DYFLEXIS_V2_API_KEY');
}
databasePort(): string {
return this.get('DATABASE_PORT');
}
databaseHost(): string {
return this.get('DATABASE_HOST');
}
databaseUser(): string {
return this.get('DATABASE_USERNAME');
}
databasePassword(): string {
return this.get('DATABASE_PASSWORD');
}
databaseName(): string {
return this.get('DATABASE_DATABASE');
}
}
@@ -0,0 +1,15 @@
import { ConfigurableModuleBuilder } from '@nestjs/common';
import { KyselyModuleAsyncOptions } from './database.module';
export const {
ConfigurableModuleClass,
MODULE_OPTIONS_TOKEN,
ASYNC_OPTIONS_TYPE,
OPTIONS_TYPE,
} = new ConfigurableModuleBuilder<KyselyModuleAsyncOptions>()
.setClassMethodName('forRoot')
.setExtras<{ isGlobal?: boolean }>(
{ isGlobal: false },
(definition, extras) => ({ ...definition, global: extras.isGlobal }),
)
.build();
@@ -1,8 +1,33 @@
import { DynamicModule, Global, Module, Provider } from '@nestjs/common';
import {
DynamicModule,
Global,
Module,
ModuleMetadata,
Provider,
Type,
} from '@nestjs/common';
import { KyselyConfig } from 'kysely';
import { createKyselyProvider } from './kysely-client.factory';
export const KYSELY_CLIENT_PROVIDER_TOKEN = 'KyselyClientProviderToken';
export const KYSELY_CONFIG_TOKEN = 'KYSELY_CONFIG_TOKEN';
export interface KyselyConfigFactory {
createKyselyConfig(): Promise<KyselyConfig> | KyselyConfig;
}
export interface KyselyModuleAsyncOptions
extends Pick<ModuleMetadata, 'imports'> {
isGlobal?: boolean;
useExisting?: Type<KyselyConfigFactory>;
useClass?: Type<KyselyConfigFactory>;
useFactory?: (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...args: any[]
) => KyselyConfig;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
inject?: any[];
}
@Global()
@Module({})
@@ -16,4 +41,83 @@ export class DatabaseModule {
providers: [provider],
};
}
public static forRootAsync(options: KyselyModuleAsyncOptions): DynamicModule {
return {
global: options.isGlobal,
module: DatabaseModule,
imports: options.imports || [],
providers: this.createAsyncProviders(options),
};
}
private static createAsyncProviders(
options: KyselyModuleAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return this.createAsyncOptionsProvider(options);
}
if (options.useClass) {
return [
...this.createAsyncOptionsProvider(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}
return [...this.createAsyncOptionsProvider(options)];
}
private static createAsyncOptionsProvider(
options: KyselyModuleAsyncOptions,
): Provider[] {
if (options.useFactory) {
const config = options.useFactory(options.inject);
return [
// {
// provide: KYSELY_CONFIG_TOKEN,
// useFactory: options.useFactory,
// inject: options.inject || [],
// },
{
provide: KYSELY_CLIENT_PROVIDER_TOKEN,
useExisting: createKyselyProvider(config)
}
];
}
if (options.useExisting) {
return [
{
provide: KYSELY_CONFIG_TOKEN,
useFactory: async (optionsFactory: KyselyConfigFactory) =>
await optionsFactory.createKyselyConfig(),
inject: [options.useExisting],
},
];
}
if (options.useClass) {
return [
{
provide: KYSELY_CONFIG_TOKEN,
useFactory: async (optionsFactory: KyselyConfigFactory) =>
await optionsFactory.createKyselyConfig(),
inject: [options.useClass],
},
];
}
return [
{
provide: KYSELY_CONFIG_TOKEN,
useFactory: async (optionsFactory: KyselyConfigFactory) =>
await optionsFactory.createKyselyConfig(),
inject: [],
},
];
}
}
@@ -1,5 +1,5 @@
import { ModuleMetadata, Type } from "@nestjs/common";
import { Prisma } from "@prisma/client";
import { ModuleMetadata, Type } from '@nestjs/common';
import { Prisma } from '@prisma/client';
export interface PrismaModuleOptions {
/**
@@ -0,0 +1,293 @@
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
input AccountingFilterArgs {
deviation: [String!]
missing: [String!]
}
enum AccountingState {
Billable
Billed
PreparedForBilling
}
type Anomaly {
createdAt: DateTime!
description: String!
groupDescription: String!
groupKey: String!
id: String!
manuallyResolved: Boolean
resolvedAt: DateTime
sleepSince: DateTime
sleepTimer: Int!
solution: [SolutionTarget!]!
tour: Tour
type: String!
workedOnBy: String!
}
type AnomalyGroup {
anomalies: [Anomaly!]!
key: String!
}
type ApprovalInfo {
answeredFrom: String
isNeeded: Boolean
location: String
whoRequested: String
}
input ApprovalInfoArgs {
answeredFrom: String
isNeeded: Boolean
location: String
whoRequested: String
}
enum ApprovalState {
APPROVED
FREE
REQUESTED
REQUIRED
UNKNOWN
}
"""
A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format.
"""
scalar DateTime
type DocumentInfo {
approval: ApprovalInfo!
tdLocation: String
}
type Interval {
end: DateTime!
start: DateTime!
}
type KPI {
meta: KPIMetadata!
value: Float!
}
type KPIByCar {
carName: String!
kpi: KPI!
}
type KPIMetadata {
availableTime: Float!
baseValue: Float!
breakEven: Float!
tourAmount: Float!
}
type Mutation {
acceptAnomaly(id: String!): Anomaly!
setAnomalyToSleep(id: String!): Anomaly!
setDocumentInfo(approval: ApprovalInfoArgs, tdLocation: String, ticketId: String!): Ticket!
}
type PaginatedAnomaly {
endCursor: String!
hasNextPage: Boolean!
nodes: [Anomaly!]!
prevCursor: String!
totalCount: Int!
}
type PaginatedAnomalyGroup {
endCursor: String!
hasNextPage: Boolean!
nodes: [AnomalyGroup!]!
prevCursor: String!
totalCount: Int!
}
type PaginatedTicket {
endCursor: String!
hasNextPage: Boolean!
nodes: [Ticket!]!
prevCursor: String!
totalCount: Int!
}
type PaginatedTour {
endCursor: String!
hasNextPage: Boolean!
nodes: [Tour!]!
prevCursor: String!
totalCount: Int!
}
type Query {
accountingStateMeta(state: AccountingState!): TourStateMeta!
accountingTours(cursor: String, filters: AccountingFilterArgs, state: AccountingState!, take: Int! = 10): PaginatedTour!
anomalies(cursor: String, take: Int! = 10): PaginatedAnomaly!
anomaliesProto: [Anomaly!]!
anomaly(id: String!): Anomaly!
groupedAnomalies(cursor: String, take: Int! = 10): PaginatedAnomalyGroup!
searchTickets(query: String!): [Ticket!]!
ticket(id: String!): Ticket!
ticketStateKeyFigures(state: TicketValidationState!): TicketStateKeyFigures!
ticketStateMeta(state: TicketValidationState!): TicketStateMeta!
tickets(cursor: String, filters: TicketFilterArgs, state: TicketValidationState!, take: Int! = 10): PaginatedTicket!
timePerTourMetricForDay(day: DateTime!, ordinanceType: String!): [TimePerTourMetric!]!
timePerTourMetricForMonth(day: DateTime!, ordinanceType: String!): [TimePerTourMetric!]!
toursPerTimeKPI(ordinanceTypes: String!): [TimePerTourKPISummary!]!
}
type SolutionTarget {
anomalyId: String!
id: String!
name: String!
steps: [SolutionTargetStep!]!
}
type SolutionTargetStep {
description: String!
id: String!
solutionTargetId: String!
}
type Subscription {
ticketsUpdated: TicketsUpdated!
timePerTourUpdated: TimePerTourUpdate!
}
type Ticket {
approvalState: ApprovalState
createdAt: DateTime!
currentState: TicketValidationState!
documentInfo: DocumentInfo
errors: [String!]!
id: String!
isUrgent: Boolean!
notes: [String!]!
tours: [Tour!]!
urgency: DateTime!
}
input TicketFilterArgs {
approvalStates: [ApprovalState!]
documentInfo: [String!]
errors: [String!]
}
type TicketStateKeyFigures {
dwellTime: Int!
}
type TicketStateMeta {
approvalNotCompanyCount: Int
approvalRequiredCount: Int
approvalUnknownCount: Int
count: Int!
tdNotCompanyCount: Int
}
enum TicketValidationState {
ARCHIVED
CREATED
DOCUMENTS_MISSING
TD_UNCERTAIN
USER_INTERACTION_REQUIRED
}
type TicketsUpdated {
count: Int!
}
type TimePerTourKPISummary {
cars: [KPIByCar!]!
interval: Interval!
kpi: KPI!
}
type TimePerTourMetric {
availableTime: Float!
averageRevenue: Float!
breakEven: Float!
categoryAmounts: [TimePerTourMetricCategoryAmount!]!
consumptionCost: Float!
forecast: Float!
forecastRevenue: Float!
interval: Interval!
personnelCost: Float!
revenue: Float!
tourAmount: Float!
}
type TimePerTourMetricCategoryAmount {
amount: Int!
category: String!
}
type TimePerTourUpdate {
job: String!
}
type Tour {
billDate: DateTime
billNumber: String
carName: String
category: String
check: Int!
codriverId: String
codriverName: String
consumptionCosts: Float!
createdAt: DateTime
deletedAt: DateTime
direction: String!
done: Boolean!
driverId: String
driverName: String
empty: String
hasInfection: Boolean
healthInsurance: String
healthInsuranceNumber: String
id: String!
infectionName: String
occupiedKm: Float!
operationId: String!
ordinanceType: String
patientCity: String
patientId: String
patientName: String
patientStreet: String
patientSurname: String
patientZip: String
rangeEndDate: DateTime
revenue: Float!
revenueDeviation: Float!
revenueDispoLive: Float!
startBegin: String
startCity: String
startDate: DateTime
startEnd: String
startInstitution: String
startStreet: String
startZip: String
target: String
targetBegin: String
targetCity: String
targetInstitution: String
targetStreet: String
targetZip: String
ticket: Ticket
ticketId: String
totalKm: Float!
transportType: String
type: String!
}
type TourStateMeta {
count: Int!
revenueSum: Float
}