fix: migrate webhook and API key REST endpoints to core schema (#13318)

## Problem
After migrating webhooks and API keys from workspace to core level, REST
API endpoints were still creating entities in workspace schema
(`workspace_*`) instead of core schema, causing webhooks to not fire.

## Solution
- Added dedicated REST controllers for webhooks (`/rest/webhooks`) and
API keys (`/rest/apiKeys`)
- Updated dynamic controller to block workspace-gated entities from
being processed
- Fixed OpenAPI documentation to exclude these endpoints from playground
- Ensured return formats match GraphQL resolvers exactly

## Testing
 All endpoints tested with provided auth token - webhooks and API keys
now correctly stored in `core` schema
This commit is contained in:
nitin
2025-07-23 18:41:53 +05:30
committed by GitHub
parent 05a09d7a73
commit 0e561e4ef4
17 changed files with 302 additions and 68 deletions
@@ -4,11 +4,21 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ApiKey } from 'src/engine/core-modules/api-key/api-key.entity';
import { ApiKeyResolver } from 'src/engine/core-modules/api-key/api-key.resolver';
import { ApiKeyService } from 'src/engine/core-modules/api-key/api-key.service';
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { ApiKeyController } from './controllers/api-key.controller';
@Module({
imports: [TypeOrmModule.forFeature([ApiKey], 'core'), JwtModule],
imports: [
TypeOrmModule.forFeature([ApiKey], 'core'),
JwtModule,
AuthModule,
WorkspaceCacheStorageModule,
],
providers: [ApiKeyService, ApiKeyResolver],
controllers: [ApiKeyController],
exports: [ApiKeyService, TypeOrmModule],
})
export class ApiKeyModule {}
@@ -0,0 +1,89 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
UseFilters,
UseGuards,
} from '@nestjs/common';
import { RestApiExceptionFilter } from 'src/engine/api/rest/rest-api-exception.filter';
import { ApiKey } from 'src/engine/core-modules/api-key/api-key.entity';
import { ApiKeyService } from 'src/engine/core-modules/api-key/api-key.service';
import { CreateApiKeyDTO } from 'src/engine/core-modules/api-key/dtos/create-api-key.dto';
import { UpdateApiKeyDTO } from 'src/engine/core-modules/api-key/dtos/update-api-key.dto';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
/**
* rest/apiKeys is deprecated, use rest/metadata/apiKeys instead
* rest/apiKeys will be removed in the future
*/
@Controller(['rest/apiKeys', 'rest/metadata/apiKeys'])
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard)
@UseFilters(RestApiExceptionFilter)
export class ApiKeyController {
constructor(private readonly apiKeyService: ApiKeyService) {}
@Get()
async findAll(@AuthWorkspace() workspace: Workspace): Promise<ApiKey[]> {
return this.apiKeyService.findActiveByWorkspaceId(workspace.id);
}
@Get(':id')
async findOne(
@Param('id') id: string,
@AuthWorkspace() workspace: Workspace,
): Promise<ApiKey | null> {
return this.apiKeyService.findById(id, workspace.id);
}
@Post()
async create(
@Body() createApiKeyDto: CreateApiKeyDTO,
@AuthWorkspace() workspace: Workspace,
): Promise<ApiKey> {
return this.apiKeyService.create({
name: createApiKeyDto.name,
expiresAt: new Date(createApiKeyDto.expiresAt),
revokedAt: createApiKeyDto.revokedAt
? new Date(createApiKeyDto.revokedAt)
: undefined,
workspaceId: workspace.id,
});
}
@Patch(':id')
async update(
@Param('id') id: string,
@Body() updateApiKeyDto: UpdateApiKeyDTO,
@AuthWorkspace() workspace: Workspace,
): Promise<ApiKey | null> {
const updateData: Partial<ApiKey> = {};
if (updateApiKeyDto.name !== undefined)
updateData.name = updateApiKeyDto.name;
if (updateApiKeyDto.expiresAt !== undefined)
updateData.expiresAt = new Date(updateApiKeyDto.expiresAt);
if (updateApiKeyDto.revokedAt !== undefined) {
updateData.revokedAt = updateApiKeyDto.revokedAt
? new Date(updateApiKeyDto.revokedAt)
: undefined;
}
return this.apiKeyService.update(id, workspace.id, updateData);
}
@Delete(':id')
async remove(
@Param('id') id: string,
@AuthWorkspace() workspace: Workspace,
): Promise<ApiKey | null> {
return this.apiKeyService.revoke(id, workspace.id);
}
}