Added auth functionality, initial work with views and field types

This commit is contained in:
Francisco Gaona
2025-12-22 03:31:55 +01:00
parent 859dca6c84
commit 0fe56c0e03
170 changed files with 11599 additions and 435 deletions

View File

@@ -1,42 +1,38 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { TenantDatabaseService } from '../tenant/tenant-database.service';
@Injectable()
export class ObjectService {
constructor(private prisma: PrismaService) {}
constructor(private tenantDbService: TenantDatabaseService) {}
// Setup endpoints - Object metadata management
async getObjectDefinitions(tenantId: string) {
return this.prisma.objectDefinition.findMany({
where: { tenantId },
include: {
fields: true,
},
orderBy: { label: 'asc' },
});
const knex = await this.tenantDbService.getTenantKnex(tenantId);
return knex('object_definitions')
.select('*')
.orderBy('label', 'asc');
}
async getObjectDefinition(tenantId: string, apiName: string) {
const obj = await this.prisma.objectDefinition.findUnique({
where: {
tenantId_apiName: {
tenantId,
apiName,
},
},
include: {
fields: {
where: { isActive: true },
orderBy: { label: 'asc' },
},
},
});
const knex = await this.tenantDbService.getTenantKnex(tenantId);
const obj = await knex('object_definitions')
.where({ apiName })
.first();
if (!obj) {
throw new NotFoundException(`Object ${apiName} not found`);
}
return obj;
// Get fields for this object
const fields = await knex('field_definitions')
.where({ objectDefinitionId: obj.id })
.orderBy('label', 'asc');
return {
...obj,
fields,
};
}
async createObjectDefinition(
@@ -49,13 +45,15 @@ export class ObjectService {
isSystem?: boolean;
},
) {
return this.prisma.objectDefinition.create({
data: {
tenantId,
...data,
tableName: `custom_${data.apiName.toLowerCase()}`,
},
const knex = await this.tenantDbService.getTenantKnex(tenantId);
const [id] = await knex('object_definitions').insert({
id: knex.raw('(UUID())'),
...data,
created_at: knex.fn.now(),
updated_at: knex.fn.now(),
});
return knex('object_definitions').where({ id }).first();
}
async createFieldDefinition(
@@ -68,20 +66,22 @@ export class ObjectService {
description?: string;
isRequired?: boolean;
isUnique?: boolean;
isLookup?: boolean;
referenceTo?: string;
referenceObject?: string;
defaultValue?: string;
options?: any;
},
) {
const knex = await this.tenantDbService.getTenantKnex(tenantId);
const obj = await this.getObjectDefinition(tenantId, objectApiName);
return this.prisma.fieldDefinition.create({
data: {
objectId: obj.id,
...data,
},
const [id] = await knex('field_definitions').insert({
id: knex.raw('(UUID())'),
objectDefinitionId: obj.id,
...data,
created_at: knex.fn.now(),
updated_at: knex.fn.now(),
});
return knex('field_definitions').where({ id }).first();
}
// Runtime endpoints - CRUD operations
@@ -91,19 +91,16 @@ export class ObjectService {
userId: string,
filters?: any,
) {
const knex = await this.tenantDbService.getTenantKnex(tenantId);
// For demonstration, using Account as example static object
if (objectApiName === 'Account') {
return this.prisma.account.findMany({
where: {
tenantId,
ownerId: userId, // Basic sharing rule
...filters,
},
});
return knex('accounts')
.where({ ownerId: userId })
.where(filters || {});
}
// For custom objects, you'd need dynamic query building
// This is a simplified version
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);
}
@@ -113,14 +110,12 @@ export class ObjectService {
recordId: string,
userId: string,
) {
const knex = await this.tenantDbService.getTenantKnex(tenantId);
if (objectApiName === 'Account') {
const record = await this.prisma.account.findFirst({
where: {
id: recordId,
tenantId,
ownerId: userId,
},
});
const record = await knex('accounts')
.where({ id: recordId, ownerId: userId })
.first();
if (!record) {
throw new NotFoundException('Record not found');
@@ -138,14 +133,18 @@ export class ObjectService {
data: any,
userId: string,
) {
const knex = await this.tenantDbService.getTenantKnex(tenantId);
if (objectApiName === 'Account') {
return this.prisma.account.create({
data: {
tenantId,
ownerId: userId,
...data,
},
const [id] = await knex('accounts').insert({
id: knex.raw('(UUID())'),
ownerId: userId,
...data,
created_at: knex.fn.now(),
updated_at: knex.fn.now(),
});
return knex('accounts').where({ id }).first();
}
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);
@@ -158,14 +157,17 @@ export class ObjectService {
data: any,
userId: string,
) {
const knex = await this.tenantDbService.getTenantKnex(tenantId);
if (objectApiName === 'Account') {
// Verify ownership
await this.getRecord(tenantId, objectApiName, recordId, userId);
return this.prisma.account.update({
where: { id: recordId },
data,
});
await knex('accounts')
.where({ id: recordId })
.update({ ...data, updated_at: knex.fn.now() });
return knex('accounts').where({ id: recordId }).first();
}
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);
@@ -177,13 +179,15 @@ export class ObjectService {
recordId: string,
userId: string,
) {
const knex = await this.tenantDbService.getTenantKnex(tenantId);
if (objectApiName === 'Account') {
// Verify ownership
await this.getRecord(tenantId, objectApiName, recordId, userId);
return this.prisma.account.delete({
where: { id: recordId },
});
await knex('accounts').where({ id: recordId }).delete();
return { success: true };
}
throw new Error(`Runtime queries for ${objectApiName} not yet implemented`);