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
11 changes: 11 additions & 0 deletions src/namespace-resources/dto/update-resource.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IsOptional,
IsNotEmpty,
} from 'class-validator';
import { UpdateResourceReqDto } from 'omniboxd/resources/dto/update-resource-req.dto';
import { ResourceType } from 'omniboxd/resources/entities/resource.entity';

export class UpdateResourceDto {
Expand Down Expand Up @@ -38,4 +39,14 @@ export class UpdateResourceDto {
@IsObject()
@IsOptional()
attrs?: Record<string, any>;

toUpdateReq(): UpdateResourceReqDto {
const updateReq = new UpdateResourceReqDto();
updateReq.name = this.name;
updateReq.parentId = this.parentId;
updateReq.tagIds = this.tag_ids ? this.tag_ids : undefined;
updateReq.content = this.content;
updateReq.attrs = this.attrs;
return updateReq;
}
}
27 changes: 4 additions & 23 deletions src/namespace-resources/namespace-resources.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ export class NamespaceResourcesService {
constructor(
@InjectRepository(Resource)
private readonly resourceRepository: Repository<Resource>,
@InjectRepository(Task)
private readonly taskRepository: Repository<Task>,
@InjectRepository(Namespace)
private readonly namespaceRepository: Repository<Namespace>,
private readonly tagService: TagService,
Expand Down Expand Up @@ -586,28 +584,11 @@ export class NamespaceResourcesService {
}

async update(userId: string, id: string, data: UpdateResourceDto) {
const resource = await this.resourceRepository.findOne({
where: { id, namespaceId: data.namespaceId },
});

if (!resource) {
throw new NotFoundException('Resource not found.');
}

// Use provided tag_ids directly
const tagIds = data.tag_ids || resource.tagIds || [];

const newResource = this.resourceRepository.create({
...resource,
...data,
namespaceId: data.namespaceId,
tagIds: tagIds.length > 0 ? tagIds : [],
});
const savedNewResource = await this.resourceRepository.save(newResource);
await this.wizardTaskService.createIndexTask(
TASK_PRIORITY,
await this.resourcesService.updateResource(
data.namespaceId,
id,
userId,
savedNewResource,
data.toUpdateReq(),
);
}

Expand Down
7 changes: 7 additions & 0 deletions src/resources/dto/update-resource-req.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class UpdateResourceReqDto {
name?: string;
parentId?: string;
tagIds?: string[];
content?: string;
attrs?: Record<string, any>;
}
3 changes: 2 additions & 1 deletion src/resources/resources.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Resource } from './entities/resource.entity';
import { ResourcesService } from './resources.service';
import { TasksModule } from 'omniboxd/tasks/tasks.module';

@Module({
imports: [TypeOrmModule.forFeature([Resource])],
imports: [TypeOrmModule.forFeature([Resource]), TasksModule],
providers: [ResourcesService],
exports: [ResourcesService],
})
Expand Down
37 changes: 37 additions & 0 deletions src/resources/resources.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Resource } from './entities/resource.entity';
import { Repository } from 'typeorm';
import { ResourceMetaDto } from './dto/resource-meta.dto';
import { UpdateResourceReqDto } from './dto/update-resource-req.dto';
import { WizardTaskService } from 'omniboxd/tasks/wizard-task.service';

const TASK_PRIORITY = 5;

@Injectable()
export class ResourcesService {
constructor(
@InjectRepository(Resource)
private readonly resourceRepository: Repository<Resource>,
private readonly wizardTaskService: WizardTaskService,
) {}

async getParentResources(
Expand Down Expand Up @@ -66,4 +71,36 @@ export class ResourcesService {
});
return children.map((r) => ResourceMetaDto.fromEntity(r));
}

async updateResource(
namespaceId: string,
resourceId: string,
userId: string,
updateReq: UpdateResourceReqDto,
) {
await this.resourceRepository.update(
{ namespaceId, id: resourceId },
{
name: updateReq.name,
parentId: updateReq.parentId,
tagIds: updateReq.tagIds,
content: updateReq.content,
attrs: updateReq.attrs,
},
);
const resource = await this.resourceRepository.findOne({
where: {
namespaceId,
id: resourceId,
},
});
if (!resource) {
throw new NotFoundException('Resource not found.');
}
await this.wizardTaskService.createIndexTask(
TASK_PRIORITY,
userId,
resource,
);
}
}
88 changes: 86 additions & 2 deletions src/tasks/task-pipeline.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { HttpStatus } from '@nestjs/common';
import { Task } from 'omniboxd/tasks/tasks.entity';
import { TaskCallbackDto } from 'omniboxd/wizard/dto/task-callback.dto';
import { isEmpty } from 'omniboxd/utils/is-empty';
import { TaskDto, TaskMetaDto } from 'omniboxd/tasks/dto/task.dto';
import {
InternalTaskDto,
TaskDto,
TaskMetaDto,
} from 'omniboxd/tasks/dto/task.dto';

/**
* Mock wizard worker that simulates the wizard worker service behavior
Expand Down Expand Up @@ -198,7 +202,10 @@ class MockWizardWorker {
* Simulates upsert_index task processing
*/
private processUpsertIndexTask(task: TaskDto): { output: any } {
console.log({ taskId: task.id, function: 'upsertIndex' });
expect(task.input).toHaveProperty('meta_info');
expect(task.input.meta_info).toHaveProperty('resource_id');
expect(task.input.meta_info).toHaveProperty('user_id');
expect(task.input.meta_info).toHaveProperty('parent_id');
return {
output: {
indexed: true,
Expand Down Expand Up @@ -290,6 +297,70 @@ describe('Task Pipeline (e2e)', () => {
});

describe('Basic Task Processing', () => {
it('create resource trigger upsertIndex', async () => {
mockWorker.startPolling();

const createResponse = (
await client
.post(`/api/v1/namespaces/${client.namespace.id}/resources`)
.send({
name: 'Test Document',
content: 'Sample content for the test document.',
parentId: client.namespace.root_resource_id,
namespaceId: client.namespace.id,
resourceType: 'doc',
})
).body;

const taskMetas: TaskMetaDto[] = (
await client.get(`/api/v1/namespaces/${client.namespace.id}/tasks`)
).body;

const upsertTaskMeta = taskMetas.find(
(t: TaskMetaDto) =>
t.function === 'upsert_index' &&
t.attrs?.resource_id === createResponse.id,
);

expect(upsertTaskMeta).toBeDefined();
const upsertTask: InternalTaskDto = (
await client.get(
`/api/v1/namespaces/${client.namespace.id}/tasks/${upsertTaskMeta?.id}`,
)
).body;
expect(upsertTask).toBeDefined();
expect(upsertTask.input.meta_info.parent_id).toBeDefined();
expect(upsertTask.input.meta_info.parent_id).not.toBeNull();

const patchResponse = await client
.patch(
`/api/v1/namespaces/${client.namespace.id}/resources/${createResponse.id}`,
)
.send({
namespaceId: client.namespace.id,
content: 'Updated content for the test document.',
});
expect(patchResponse.status).toBe(200);
const patchTaskMetas: TaskMetaDto[] = (
await client.get(`/api/v1/namespaces/${client.namespace.id}/tasks`)
).body;
const patchUpsertTaskMeta = patchTaskMetas.find(
(t: TaskMetaDto) =>
t.function === 'upsert_index' &&
t.attrs?.resource_id === createResponse.id &&
t.id !== upsertTaskMeta?.id,
);
expect(patchUpsertTaskMeta).toBeDefined();
const patchUpsertTask: InternalTaskDto = (
await client.get(
`/api/v1/namespaces/${client.namespace.id}/tasks/${patchUpsertTaskMeta?.id}`,
)
).body;
expect(patchUpsertTask).toBeDefined();
expect(patchUpsertTask.input.meta_info.parent_id).toBeDefined();
expect(patchUpsertTask.input.meta_info.parent_id).not.toBeNull();
});

it('should process a collect task end-to-end', async () => {
// Start the mock worker
mockWorker.startPolling();
Expand Down Expand Up @@ -343,6 +414,19 @@ describe('Task Pipeline (e2e)', () => {
expect(resourceResponse.status).toBe(200);
expect(resourceResponse.body.id).toBe(resourceId);
expect(resourceResponse.body.content).toContain('Test Page Title');

const taskMetas: TaskMetaDto[] = (
await client.get(`/api/v1/namespaces/${client.namespace.id}/tasks`)
).body;
const upsertTaskMeta = taskMetas.find(
(t: TaskMetaDto) => t.function === 'upsert_index',
);
const upsertTask: InternalTaskDto = (
await client.get(
`/api/v1/namespaces/${client.namespace.id}/tasks/${upsertTaskMeta?.id}`,
)
).body;
expect(upsertTask).toBeDefined();
});

it('should handle task exceptions properly', async () => {
Expand Down
31 changes: 20 additions & 11 deletions src/wizard/processors/collect.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Processor } from 'omniboxd/wizard/processors/processor.abstract';
import { isEmpty } from 'omniboxd/utils/is-empty';
import { TagService } from 'omniboxd/tag/tag.service';
import { ProcessedImage } from 'omniboxd/wizard/types/wizard.types';
import { UpdateResourceDto } from 'omniboxd/namespace-resources/dto/update-resource.dto';

export class CollectProcessor extends Processor {
constructor(
Expand All @@ -20,10 +21,14 @@ export class CollectProcessor extends Processor {
throw new BadRequestException('Invalid task payload');
}
if (task.exception && !isEmpty(task.exception)) {
await this.namespaceResourcesService.update(task.userId, resourceId, {
namespaceId: task.namespaceId,
content: task.exception.error,
});
await this.namespaceResourcesService.update(
task.userId,
resourceId,
Object.assign(new UpdateResourceDto(), {
namespaceId: task.namespaceId,
content: task.exception.error,
}),
);
return {};
} else if (task.output) {
const { markdown, title, ...attrs } = task.output || {};
Expand Down Expand Up @@ -51,13 +56,17 @@ export class CollectProcessor extends Processor {

const resource = await this.namespaceResourcesService.get(resourceId);
const mergedAttrs = { ...(resource?.attrs || {}), ...attrs };
await this.namespaceResourcesService.update(task.userId, resourceId, {
namespaceId: task.namespaceId,
name: title,
content: processedMarkdown,
attrs: mergedAttrs,
tag_ids: tagIds,
});
await this.namespaceResourcesService.update(
task.userId,
resourceId,
Object.assign(new UpdateResourceDto(), {
namespaceId: task.namespaceId,
name: title,
content: processedMarkdown,
attrs: mergedAttrs,
tag_ids: tagIds,
}),
);
return { resourceId, tagIds };
}
return {};
Expand Down
13 changes: 9 additions & 4 deletions src/wizard/processors/extract-tags.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TagService } from 'omniboxd/tag/tag.service';
import { BadRequestException } from '@nestjs/common';
import { isEmpty } from 'omniboxd/utils/is-empty';
import { ExtractTagsOutputDto } from 'omniboxd/wizard/processors/dto/extract-tags.output.dto';
import { UpdateResourceDto } from 'omniboxd/namespace-resources/dto/update-resource.dto';

export class ExtractTagsProcessor extends Processor {
constructor(
Expand Down Expand Up @@ -38,10 +39,14 @@ export class ExtractTagsProcessor extends Processor {
);

// Update the resource with extracted tag IDs from external service
await this.namespaceResourcesService.update(task.userId, resourceId, {
namespaceId: task.namespaceId,
tag_ids: tagIds,
});
await this.namespaceResourcesService.update(
task.userId,
resourceId,
Object.assign(new UpdateResourceDto(), {
namespaceId: task.namespaceId,
tag_ids: tagIds,
}),
);

return { resourceId, tags: tagNames, tagIds };
}
Expand Down
13 changes: 9 additions & 4 deletions src/wizard/processors/generate-title.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Processor } from 'omniboxd/wizard/processors/processor.abstract';
import { NamespaceResourcesService } from 'omniboxd/namespace-resources/namespace-resources.service';
import { BadRequestException } from '@nestjs/common';
import { isEmpty } from 'omniboxd/utils/is-empty';
import { UpdateResourceDto } from 'omniboxd/namespace-resources/dto/update-resource.dto';

export class GenerateTitleProcessor extends Processor {
constructor(
Expand All @@ -28,10 +29,14 @@ export class GenerateTitleProcessor extends Processor {

if (typeof generatedTitle === 'string' && generatedTitle.trim()) {
// Update the resource with generated title
await this.namespaceResourcesService.update(task.userId, resourceId, {
namespaceId: task.namespaceId,
name: generatedTitle.trim(),
});
await this.namespaceResourcesService.update(
task.userId,
resourceId,
Object.assign(new UpdateResourceDto(), {
namespaceId: task.namespaceId,
name: generatedTitle.trim(),
}),
);

return { resourceId, title: generatedTitle.trim() };
}
Expand Down