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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as fs from 'fs';
import * as path from 'path';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { NamingUtil } from '../../utils/naming.util';

interface GeneratorConfig {
Expand Down Expand Up @@ -62,12 +62,18 @@ export class RepositoryGenerator {
const templatePath = path.join(this.templatesDir, templateName);
let template = fs.readFileSync(templatePath, 'utf-8');

template = template.replace(
/{{ENTITY_NAME_PASCAL}}/g,
template = template.replaceAll(
'{{ENTITY_NAME_PASCAL}}',
this.entityNamePascal,
);
template = template.replace(/{{ENTITY_NAME_KEBAB}}/g, this.entityNameKebab);
template = template.replace(/{{ENTITY_NAME_CAMEL}}/g, this.entityNameCamel);
template = template.replaceAll(
'{{ENTITY_NAME_KEBAB}}',
this.entityNameKebab,
);
template = template.replaceAll(
'{{ENTITY_NAME_CAMEL}}',
this.entityNameCamel,
);

return template;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as fs from 'fs';
import * as path from 'path';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { NamingUtil } from '../../utils/naming.util';

interface GeneratorConfig {
Expand Down Expand Up @@ -62,12 +62,18 @@ export class RepositoryGenerator {
const templatePath = path.join(this.templatesDir, templateName);
let template = fs.readFileSync(templatePath, 'utf-8');

template = template.replace(
/{{ENTITY_NAME_PASCAL}}/g,
template = template.replaceAll(
'{{ENTITY_NAME_PASCAL}}',
this.entityNamePascal,
);
template = template.replace(/{{ENTITY_NAME_KEBAB}}/g, this.entityNameKebab);
template = template.replace(/{{ENTITY_NAME_CAMEL}}/g, this.entityNameCamel);
template = template.replaceAll(
'{{ENTITY_NAME_KEBAB}}',
this.entityNameKebab,
);
template = template.replaceAll(
'{{ENTITY_NAME_CAMEL}}',
this.entityNameCamel,
);

return template;
}
Expand Down
6 changes: 3 additions & 3 deletions apps/api/scripts/code-generation/utils/naming.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ export class NamingUtil {
static toKebabCase(str: string): string {
return str
.trim()
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/[\s_]+/g, '-')
.replaceAll(/([a-z])([A-Z])/g, '$1-$2')
.replaceAll(/[\s_]+/g, '-')
.toLowerCase();
}

static toPascalCase(str: string): string {
return str
.trim()
.split(/[-_\s]+/)
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join('');
}

Expand Down
82 changes: 1 addition & 81 deletions apps/api/src/decorators/class/auto-transaction.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,12 @@ import {
type TransactionalAdapter,
} from '@nestjs-cls/transactional';
import { NO_TRANSACTION_KEY } from '../constants';
import { mkdirSync, writeFileSync } from 'fs';
import { join } from 'path';

type TOptionsFromAdapter<TAdapter> =
TAdapter extends TransactionalAdapter<any, any, infer TOptions>
? TOptions
: never;

class TransactionalLogger {
private readonly processDir: string;

constructor() {
const baseLogDir = join(process.cwd(), 'tmp', 'transaction');

const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const random = Math.random().toString(36).substring(2, 8);
const processId = `${timestamp}_${random}`;

this.processDir = join(baseLogDir, processId);
mkdirSync(this.processDir, { recursive: true });

this.log('Session', 'Transaction validation session started');
this.log('Session', `Process ID: ${processId}`);
this.log('Session', `Log directory: ${this.processDir}`);
}

log(className: string, message: string): void {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] ${message}`;

const classLogFile = join(this.processDir, `${className}.log`);

writeFileSync(classLogFile, logMessage + '\n', {
flag: 'a',
encoding: 'utf-8',
});

// console.log(`[${timestamp}] [${className}] ${message}`);
}

getProcessDir(): string {
return this.processDir;
}
}

const logger = new TransactionalLogger();

/**
* Run the decorated class methods in a transaction.
*
Expand Down Expand Up @@ -120,7 +79,7 @@ export function AutoTransaction<TAdapter = any>(
): ClassDecorator {
return (target) => {
if (typeof target !== 'function') {
throw new Error(
throw new TypeError(
`@AutoTransaction can only be used on classes, but the target is not a function.`,
);
}
Expand All @@ -140,23 +99,17 @@ export function AutoTransaction<TAdapter = any>(
);
}

const methodsProcessed: string[] = [];
const methodsSkipped: string[] = [];
const methodsWithNoTransaction: string[] = [];

for (const name of Object.getOwnPropertyNames(proto)) {
if (name === 'constructor') {
continue;
}

const descriptor = Object.getOwnPropertyDescriptor(proto, name);
if (!descriptor) {
methodsSkipped.push(`${name} (no descriptor)`);
continue;
}

if (typeof descriptor.value !== 'function') {
methodsSkipped.push(`${name} (not a function)`);
continue;
}

Expand All @@ -166,7 +119,6 @@ export function AutoTransaction<TAdapter = any>(
);

if (noTransaction) {
methodsWithNoTransaction.push(name);
continue;
}

Expand All @@ -186,39 +138,7 @@ export function AutoTransaction<TAdapter = any>(
);
}

logger.log(className, `✓ ${name}: Function wrapped in transaction proxy`);

Object.defineProperty(proto, name, descriptor);

methodsProcessed.push(name);
}

logger.log(className, `@AutoTransaction decorator applied`);

logger.log(className, `Summary:`);
logger.log(
className,
` - Methods wrapped: ${methodsProcessed.length} (${methodsProcessed.join(', ') || 'none'})`,
);
logger.log(
className,
` - Methods with @NoTransaction: ${methodsWithNoTransaction.length} (${methodsWithNoTransaction.join(', ') || 'none'})`,
);
logger.log(
className,
` - Methods skipped: ${methodsSkipped.length} (${methodsSkipped.join(', ') || 'none'})`,
);

if (
methodsProcessed.length === 0 &&
methodsWithNoTransaction.length === 0
) {
logger.log(
className,
`⚠ Warning: No methods were wrapped. This may indicate the decorator is applied to a class with no methods.`,
);
}

logger.log(className, `Process directory: ${logger.getProcessDir()}`);
};
}
5 changes: 2 additions & 3 deletions apps/api/src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ export class AuthService {
});

if (
!token ||
!token.accessToken ||
!token?.accessToken ||
!token.accessTokenExpiresAt ||
DateExtensions.hasDatePassed(token.accessTokenExpiresAt)
) {
Expand Down Expand Up @@ -121,7 +120,7 @@ export class AuthService {
},
});

if (!tokens || !tokens.accessToken) {
if (!tokens?.accessToken) {
throw new Error(
`Failed to refresh token for user ${account.userId} and provider ${provider}. User may need to re-authenticate.`,
);
Expand Down
4 changes: 3 additions & 1 deletion apps/api/src/modules/crud/schemas/crud.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export const ZUpdateCrudDto = ZCrud.pick({
content: true,
});

export const ZCrudCreateRequest = ZBaseRequest.merge(ZCreateCrudDto);
export const ZCrudCreateRequest = ZBaseRequest.extend({
content: z.string().min(1).max(1000),
});

export const ZCrudCreateResponse = ZBaseResponse.extend({
id: z.string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import { MongooseBaseEntity } from './mongoose.base-entity';
export abstract class MongooseBaseRepository<
TDomainEntity extends BaseEntity,
TDbEntity extends MongooseBaseEntity,
> implements IMongooseRepository<TDomainEntity, TDbEntity>
{
> implements IMongooseRepository<TDomainEntity, TDbEntity> {
protected readonly model: Model<TDbEntity>;
protected readonly mongoTxHost: TransactionHost<TransactionalAdapterMongoose>;

Expand Down
6 changes: 1 addition & 5 deletions apps/api/src/schemas/base.schema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { z } from 'zod';

export const ZBaseRequest = z.object({
requestId: z.string().uuid().optional(),
timestamp: z.number().optional(),
});

export const ZBaseRequest = z.object({});
export const ZBaseResponse = z.object({
success: z.boolean(),
message: z.string().optional(),
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/app/crud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ const styles = StyleSheet.create({
},
});

function CrudPanel({ dbType }: { dbType: DbType }) {
function CrudPanel({ dbType }: Readonly<{ dbType: DbType }>) {
const utils = trpc.useUtils();
const [content, setContent] = useState("");
const [editingId, setEditingId] = useState<string | null>(null);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/crud-demo/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface CrudItem {
content: string;
}

function CrudPanel({ dbType }: { dbType: DbType }) {
function CrudPanel({ dbType }: Readonly<{ dbType: DbType }>) {
const utils = trpc.useUtils();
const [content, setContent] = useState("");
const [editingId, setEditingId] = useState<string | null>(null);
Expand Down
3 changes: 2 additions & 1 deletion packages/sonarqube/sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ sonar.exclusions=\
**/vendor/**,\
**/tmp/**,\
**/temp/**,\
**/.cache/**
**/.cache/**,\
packages/trpc/src/server/server.ts

# Exclude specific directories per package
# sonar.exclusions=apps/web/dist/**,apps/api/dist/**,packages/*/dist/**
Expand Down
Loading