@ftisindia/create-app 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/.env.example +31 -0
- package/template/README.md +61 -0
- package/template/_gitignore +6 -0
- package/template/_package.json +6 -0
- package/template/docs/FORMS.md +169 -0
- package/template/docs/FORMS_CHECKLIST.md +61 -0
- package/template/docs/REPORTS.md +246 -0
- package/template/docs/REPORTS_CHECKLIST.md +97 -0
- package/template/prisma/migrations/20260612000000_add_form_builder/migration.sql +147 -0
- package/template/prisma/migrations/20260613000000_add_report_builder/migration.sql +129 -0
- package/template/prisma/schema.prisma +285 -0
- package/template/scripts/export-openapi.ts +85 -0
- package/template/scripts/gen-form.mjs +149 -0
- package/template/scripts/push-form.ts +124 -0
- package/template/src/app.module.ts +29 -8
- package/template/src/common/dto/membership-response.dto.ts +1 -0
- package/template/src/common/dto/role-summary.dto.ts +3 -3
- package/template/src/common/dto/user-summary.dto.ts +3 -3
- package/template/src/config/app.config.ts +6 -1
- package/template/src/config/env.validation.ts +45 -0
- package/template/src/config/forms.config.ts +12 -0
- package/template/src/config/index.ts +2 -0
- package/template/src/config/openapi.ts +12 -0
- package/template/src/config/reports-secret.ts +15 -0
- package/template/src/config/reports.config.ts +16 -0
- package/template/src/main.ts +16 -12
- package/template/src/modules/access-control/access-control.module.ts +2 -1
- package/template/src/modules/access-control/dto/access-control-response.dto.ts +3 -0
- package/template/src/modules/access-control/dto/current-access-control-response.dto.ts +35 -0
- package/template/src/modules/access-control/presentation/current-access-control.controller.ts +40 -0
- package/template/src/modules/access-control/types/permission-key.ts +27 -0
- package/template/src/modules/access-control/types/route-permission-registry.ts +183 -0
- package/template/src/modules/audit/dto/audit-response.dto.ts +7 -3
- package/template/src/modules/auth/auth.module.ts +3 -1
- package/template/src/modules/auth/dto/auth-response.dto.ts +1 -1
- package/template/src/modules/forms/application/services/file-gc.service.ts +85 -0
- package/template/src/modules/forms/application/services/forms-definitions.service.ts +137 -0
- package/template/src/modules/forms/application/services/forms-error.mapper.ts +64 -0
- package/template/src/modules/forms/application/services/forms-export.service.ts +210 -0
- package/template/src/modules/forms/application/services/forms-files.service.ts +164 -0
- package/template/src/modules/forms/application/services/forms-public.service.ts +49 -0
- package/template/src/modules/forms/application/services/forms-settings-reader.service.ts +53 -0
- package/template/src/modules/forms/application/services/forms-submissions.service.ts +103 -0
- package/template/src/modules/forms/application/services/handlers/authenticate.action.ts +37 -0
- package/template/src/modules/forms/application/services/handlers/logging-email.handler.ts +22 -0
- package/template/src/modules/forms/application/services/handlers/send-confirmation-email.action.ts +40 -0
- package/template/src/modules/forms/application/services/handlers/webhook.handler.ts +41 -0
- package/template/src/modules/forms/application/services/outbox-dispatcher.service.ts +109 -0
- package/template/src/modules/forms/dto/create-form-definition.dto.ts +12 -0
- package/template/src/modules/forms/dto/data-source-response.dto.ts +19 -0
- package/template/src/modules/forms/dto/export-submissions-query.dto.ts +33 -0
- package/template/src/modules/forms/dto/file-upload-response.dto.ts +24 -0
- package/template/src/modules/forms/dto/form-definition-response.dto.ts +50 -0
- package/template/src/modules/forms/dto/form-render-response.dto.ts +17 -0
- package/template/src/modules/forms/dto/list-form-definitions-query.dto.ts +10 -0
- package/template/src/modules/forms/dto/list-submissions-query.dto.ts +10 -0
- package/template/src/modules/forms/dto/public-submit-form.dto.ts +24 -0
- package/template/src/modules/forms/dto/set-public-access.dto.ts +8 -0
- package/template/src/modules/forms/dto/submission-response.dto.ts +99 -0
- package/template/src/modules/forms/dto/submit-form.dto.ts +50 -0
- package/template/src/modules/forms/dto/update-form-definition.dto.ts +12 -0
- package/template/src/modules/forms/dto/upload-file-query.dto.ts +33 -0
- package/template/src/modules/forms/dto/validate-submission.dto.ts +22 -0
- package/template/src/modules/forms/examples/abstract-submission.form.json +80 -0
- package/template/src/modules/forms/examples/login.form.json +24 -0
- package/template/src/modules/forms/examples/registration.form.json +44 -0
- package/template/src/modules/forms/forms.module.ts +226 -0
- package/template/src/modules/forms/forms.tokens.ts +6 -0
- package/template/src/modules/forms/infrastructure/audit-sink.adapter.ts +30 -0
- package/template/src/modules/forms/infrastructure/casl-forms-authorization.ts +31 -0
- package/template/src/modules/forms/infrastructure/prisma-tx-runner.ts +17 -0
- package/template/src/modules/forms/infrastructure/registry/form-extension.decorators.ts +17 -0
- package/template/src/modules/forms/infrastructure/registry/registry-bootstrap.service.ts +82 -0
- package/template/src/modules/forms/infrastructure/request-forms-context.ts +60 -0
- package/template/src/modules/forms/infrastructure/schema-check/forms-schema-check.service.ts +76 -0
- package/template/src/modules/forms/infrastructure/storage/local-disk-storage.adapter.ts +43 -0
- package/template/src/modules/forms/infrastructure/stores/index.ts +5 -0
- package/template/src/modules/forms/infrastructure/stores/prisma-action-log.store.ts +37 -0
- package/template/src/modules/forms/infrastructure/stores/prisma-file.store.ts +108 -0
- package/template/src/modules/forms/infrastructure/stores/prisma-form-definition.store.ts +147 -0
- package/template/src/modules/forms/infrastructure/stores/prisma-outbox.store.ts +133 -0
- package/template/src/modules/forms/infrastructure/stores/prisma-submission.store.ts +164 -0
- package/template/src/modules/forms/presentation/forms-data-sources.controller.ts +58 -0
- package/template/src/modules/forms/presentation/forms-definitions.controller.ts +191 -0
- package/template/src/modules/forms/presentation/forms-files.controller.ts +79 -0
- package/template/src/modules/forms/presentation/forms-submissions.controller.ts +154 -0
- package/template/src/modules/forms/presentation/forms-upload.interceptor.ts +33 -0
- package/template/src/modules/forms/presentation/public-forms.controller.ts +51 -0
- package/template/src/modules/invitations/dto/invitation-response.dto.ts +4 -0
- package/template/src/modules/organisations/application/services/organisations.service.ts +67 -1
- package/template/src/modules/organisations/dto/organisation-response.dto.ts +52 -0
- package/template/src/modules/organisations/presentation/organisations.controller.ts +25 -3
- package/template/src/modules/reports/application/services/reports-actions.service.ts +54 -0
- package/template/src/modules/reports/application/services/reports-definitions.service.ts +66 -0
- package/template/src/modules/reports/application/services/reports-error.mapper.ts +97 -0
- package/template/src/modules/reports/application/services/reports-export-dispatcher.service.ts +124 -0
- package/template/src/modules/reports/application/services/reports-exports.service.ts +74 -0
- package/template/src/modules/reports/application/services/reports-queries.service.ts +35 -0
- package/template/src/modules/reports/application/services/reports-settings-reader.service.ts +49 -0
- package/template/src/modules/reports/application/services/reports-views.service.ts +79 -0
- package/template/src/modules/reports/dto/action-result-response.dto.ts +21 -0
- package/template/src/modules/reports/dto/create-report-definition.dto.ts +86 -0
- package/template/src/modules/reports/dto/create-saved-view.dto.ts +26 -0
- package/template/src/modules/reports/dto/execute-action.dto.ts +71 -0
- package/template/src/modules/reports/dto/export-job-response.dto.ts +60 -0
- package/template/src/modules/reports/dto/export-request.dto.ts +34 -0
- package/template/src/modules/reports/dto/list-reports-query.dto.ts +10 -0
- package/template/src/modules/reports/dto/list-views-query.dto.ts +17 -0
- package/template/src/modules/reports/dto/prepare-action-response.dto.ts +14 -0
- package/template/src/modules/reports/dto/prepare-action.dto.ts +27 -0
- package/template/src/modules/reports/dto/query-response.dto.ts +64 -0
- package/template/src/modules/reports/dto/query-spec.dto.ts +120 -0
- package/template/src/modules/reports/dto/report-definition-response.dto.ts +64 -0
- package/template/src/modules/reports/dto/report-meta-query.dto.ts +16 -0
- package/template/src/modules/reports/dto/report-meta-response.dto.ts +113 -0
- package/template/src/modules/reports/dto/saved-view-response.dto.ts +66 -0
- package/template/src/modules/reports/dto/update-report-definition.dto.ts +9 -0
- package/template/src/modules/reports/dto/update-saved-view.dto.ts +27 -0
- package/template/src/modules/reports/examples/abstract-review-board.report.json +54 -0
- package/template/src/modules/reports/examples/org-members.report.json +55 -0
- package/template/src/modules/reports/infrastructure/audit-sink.adapter.ts +31 -0
- package/template/src/modules/reports/infrastructure/casl-reports-authorization.ts +39 -0
- package/template/src/modules/reports/infrastructure/forms-adapter/form-report-source.adapter.ts +292 -0
- package/template/src/modules/reports/infrastructure/forms-adapter/form-row-actions.ts +171 -0
- package/template/src/modules/reports/infrastructure/forms-adapter/forms-bridge-bootstrap.service.ts +32 -0
- package/template/src/modules/reports/infrastructure/prisma-catalog.adapter.ts +95 -0
- package/template/src/modules/reports/infrastructure/prisma-query-executor.ts +103 -0
- package/template/src/modules/reports/infrastructure/prisma-snapshot-runner.ts +47 -0
- package/template/src/modules/reports/infrastructure/prisma-tx-runner.ts +18 -0
- package/template/src/modules/reports/infrastructure/registry/registry-bootstrap.service.ts +61 -0
- package/template/src/modules/reports/infrastructure/registry/report-extension.decorators.ts +14 -0
- package/template/src/modules/reports/infrastructure/reports-job-queue.adapter.ts +28 -0
- package/template/src/modules/reports/infrastructure/request-reports-context.ts +42 -0
- package/template/src/modules/reports/infrastructure/schema-check/reports-schema-check.service.ts +116 -0
- package/template/src/modules/reports/infrastructure/storage/local-disk-export-storage.adapter.ts +79 -0
- package/template/src/modules/reports/infrastructure/stores/index.ts +5 -0
- package/template/src/modules/reports/infrastructure/stores/prisma-bulk-action-run.store.ts +89 -0
- package/template/src/modules/reports/infrastructure/stores/prisma-export-job.store.ts +93 -0
- package/template/src/modules/reports/infrastructure/stores/prisma-report-definition.store.ts +171 -0
- package/template/src/modules/reports/infrastructure/stores/prisma-row-tag.store.ts +110 -0
- package/template/src/modules/reports/infrastructure/stores/prisma-saved-view.store.ts +144 -0
- package/template/src/modules/reports/presentation/reports-actions.controller.ts +83 -0
- package/template/src/modules/reports/presentation/reports-definitions.controller.ts +156 -0
- package/template/src/modules/reports/presentation/reports-export-jobs.controller.ts +61 -0
- package/template/src/modules/reports/presentation/reports-export.controller.ts +76 -0
- package/template/src/modules/reports/presentation/reports-query.controller.ts +52 -0
- package/template/src/modules/reports/presentation/reports-views.controller.ts +140 -0
- package/template/src/modules/reports/reports-forms.module.ts +33 -0
- package/template/src/modules/reports/reports.module.ts +335 -0
- package/template/src/modules/reports/reports.tokens.ts +11 -0
- package/template/src/modules/reports/sources/org-members.source.ts +112 -0
- package/template/src/modules/settings/types/setting-definitions.ts +94 -0
- package/template/test/forms-definitions.e2e-spec.ts +394 -0
- package/template/test/forms-export.e2e-spec.ts +390 -0
- package/template/test/forms-files.e2e-spec.ts +345 -0
- package/template/test/forms-outbox.e2e-spec.ts +309 -0
- package/template/test/forms-permission-sync.spec.ts +27 -0
- package/template/test/forms-public.e2e-spec.ts +269 -0
- package/template/test/forms-schema-check.e2e-spec.ts +65 -0
- package/template/test/forms-submissions.e2e-spec.ts +500 -0
- package/template/test/forms-webhooks.e2e-spec.ts +261 -0
- package/template/test/frontend-bootstrap.spec.ts +181 -0
- package/template/test/reports-advanced.e2e-spec.ts +368 -0
- package/template/test/reports-permission-sync.spec.ts +30 -0
- package/template/test/reports-query.e2e-spec.ts +350 -0
- package/template/test/reports-tiers.e2e-spec.ts +257 -0
- package/template/test/route-registry.validator.spec.ts +34 -0
- package/template/test/security.e2e-spec.ts +134 -2
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import {
|
|
3
|
+
Allow,
|
|
4
|
+
ArrayMinSize,
|
|
5
|
+
IsArray,
|
|
6
|
+
IsIn,
|
|
7
|
+
IsObject,
|
|
8
|
+
IsOptional,
|
|
9
|
+
IsString,
|
|
10
|
+
Matches,
|
|
11
|
+
} from 'class-validator';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The request body IS the ReportDefinition document (report design §2/§10).
|
|
15
|
+
* Envelope-only validation — the same two-layer split as forms: this DTO
|
|
16
|
+
* whitelists the document's top-level keys (the global ValidationPipe runs
|
|
17
|
+
* with forbidNonWhitelisted) and checks the cheap invariants; the engine's
|
|
18
|
+
* meta-schema + linter are the real gate for everything nested (columns,
|
|
19
|
+
* source binding, export config, ...), so nested content passes through
|
|
20
|
+
* untouched.
|
|
21
|
+
*/
|
|
22
|
+
export class CreateReportDefinitionDto {
|
|
23
|
+
@ApiProperty({ example: 'abstract-review-board' })
|
|
24
|
+
@IsString()
|
|
25
|
+
@Matches(/^[a-z][a-z0-9-]*$/)
|
|
26
|
+
key!: string;
|
|
27
|
+
|
|
28
|
+
@ApiPropertyOptional({ example: 'Abstract review board' })
|
|
29
|
+
@IsOptional()
|
|
30
|
+
@IsString()
|
|
31
|
+
title?: string;
|
|
32
|
+
|
|
33
|
+
@ApiPropertyOptional({ example: 'All submitted abstracts with review status.' })
|
|
34
|
+
@IsOptional()
|
|
35
|
+
@IsString()
|
|
36
|
+
description?: string;
|
|
37
|
+
|
|
38
|
+
@ApiProperty({
|
|
39
|
+
type: 'object',
|
|
40
|
+
additionalProperties: true,
|
|
41
|
+
description: 'Source binding ({ kind, key, options? }) — resolved and validated by the engine.',
|
|
42
|
+
})
|
|
43
|
+
@IsObject()
|
|
44
|
+
source!: Record<string, unknown>;
|
|
45
|
+
|
|
46
|
+
@ApiProperty({
|
|
47
|
+
type: 'array',
|
|
48
|
+
items: { type: 'object', additionalProperties: true },
|
|
49
|
+
description: 'Column definitions — validated against the engine meta-schema and linter.',
|
|
50
|
+
})
|
|
51
|
+
@IsArray()
|
|
52
|
+
@ArrayMinSize(1)
|
|
53
|
+
columns!: Record<string, unknown>[];
|
|
54
|
+
|
|
55
|
+
@ApiPropertyOptional({
|
|
56
|
+
type: 'array',
|
|
57
|
+
items: { type: 'object', additionalProperties: true },
|
|
58
|
+
})
|
|
59
|
+
@Allow()
|
|
60
|
+
defaultSort?: unknown;
|
|
61
|
+
|
|
62
|
+
@ApiPropertyOptional({ type: 'object', additionalProperties: true })
|
|
63
|
+
@Allow()
|
|
64
|
+
search?: unknown;
|
|
65
|
+
|
|
66
|
+
@ApiPropertyOptional({ type: [String] })
|
|
67
|
+
@Allow()
|
|
68
|
+
rowActions?: unknown;
|
|
69
|
+
|
|
70
|
+
@ApiPropertyOptional({ type: 'object', additionalProperties: true })
|
|
71
|
+
@Allow()
|
|
72
|
+
export?: unknown;
|
|
73
|
+
|
|
74
|
+
@ApiProperty({ enum: ['live', 'indexed', 'materialized'], example: 'indexed' })
|
|
75
|
+
@IsIn(['live', 'indexed', 'materialized'])
|
|
76
|
+
performanceTier!: 'live' | 'indexed' | 'materialized';
|
|
77
|
+
|
|
78
|
+
// Server-assigned; tolerated on input so fetched documents round-trip.
|
|
79
|
+
@ApiPropertyOptional({ description: 'Server-assigned; ignored on input.' })
|
|
80
|
+
@Allow()
|
|
81
|
+
version?: unknown;
|
|
82
|
+
|
|
83
|
+
@ApiPropertyOptional({ description: 'Server-assigned; ignored on input.' })
|
|
84
|
+
@Allow()
|
|
85
|
+
status?: unknown;
|
|
86
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
2
|
+
import { Type } from 'class-transformer';
|
|
3
|
+
import {
|
|
4
|
+
IsDefined,
|
|
5
|
+
IsObject,
|
|
6
|
+
IsString,
|
|
7
|
+
MaxLength,
|
|
8
|
+
MinLength,
|
|
9
|
+
ValidateNested,
|
|
10
|
+
} from 'class-validator';
|
|
11
|
+
import { QuerySpecDto } from './query-spec.dto';
|
|
12
|
+
|
|
13
|
+
export class CreateSavedViewDto {
|
|
14
|
+
@ApiProperty({ example: 'My ML shortlist', minLength: 1, maxLength: 120 })
|
|
15
|
+
@IsString()
|
|
16
|
+
@MinLength(1)
|
|
17
|
+
@MaxLength(120)
|
|
18
|
+
name!: string;
|
|
19
|
+
|
|
20
|
+
@ApiProperty({ type: QuerySpecDto })
|
|
21
|
+
@IsDefined()
|
|
22
|
+
@IsObject()
|
|
23
|
+
@ValidateNested()
|
|
24
|
+
@Type(() => QuerySpecDto)
|
|
25
|
+
spec!: QuerySpecDto;
|
|
26
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { Type } from 'class-transformer';
|
|
3
|
+
import {
|
|
4
|
+
ArrayMaxSize,
|
|
5
|
+
IsArray,
|
|
6
|
+
IsObject,
|
|
7
|
+
IsOptional,
|
|
8
|
+
IsString,
|
|
9
|
+
MaxLength,
|
|
10
|
+
MinLength,
|
|
11
|
+
ValidateNested,
|
|
12
|
+
} from 'class-validator';
|
|
13
|
+
import { QuerySpecDto } from './query-spec.dto';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Bulk selection (§6.3): exactly one of byIds / byFilter — enforced by the
|
|
17
|
+
* engine. byIds is self-snapshotting and capped; byFilter requires the
|
|
18
|
+
* prepared action token because rows matching at confirmation may differ at
|
|
19
|
+
* execution.
|
|
20
|
+
*/
|
|
21
|
+
export class ExecuteActionSelectionDto {
|
|
22
|
+
@ApiPropertyOptional({ type: [String], maxItems: 1000 })
|
|
23
|
+
@IsOptional()
|
|
24
|
+
@IsArray()
|
|
25
|
+
@ArrayMaxSize(1000)
|
|
26
|
+
@IsString({ each: true })
|
|
27
|
+
byIds?: string[];
|
|
28
|
+
|
|
29
|
+
@ApiPropertyOptional({ type: QuerySpecDto })
|
|
30
|
+
@IsOptional()
|
|
31
|
+
@IsObject()
|
|
32
|
+
@ValidateNested()
|
|
33
|
+
@Type(() => QuerySpecDto)
|
|
34
|
+
byFilter?: QuerySpecDto;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class ExecuteActionDto {
|
|
38
|
+
@ApiPropertyOptional({ type: ExecuteActionSelectionDto })
|
|
39
|
+
@IsOptional()
|
|
40
|
+
@IsObject()
|
|
41
|
+
@ValidateNested()
|
|
42
|
+
@Type(() => ExecuteActionSelectionDto)
|
|
43
|
+
selection?: ExecuteActionSelectionDto;
|
|
44
|
+
|
|
45
|
+
@ApiPropertyOptional({
|
|
46
|
+
description: 'Prepared action token — REQUIRED for byFilter selections (§6.3).',
|
|
47
|
+
})
|
|
48
|
+
@IsOptional()
|
|
49
|
+
@IsString()
|
|
50
|
+
actionToken?: string;
|
|
51
|
+
|
|
52
|
+
@ApiPropertyOptional({
|
|
53
|
+
type: 'object',
|
|
54
|
+
additionalProperties: true,
|
|
55
|
+
description: "Action input — validated against the action's JSON Schema by the engine.",
|
|
56
|
+
})
|
|
57
|
+
@IsOptional()
|
|
58
|
+
@IsObject()
|
|
59
|
+
input?: Record<string, unknown>;
|
|
60
|
+
|
|
61
|
+
@ApiPropertyOptional({
|
|
62
|
+
minLength: 1,
|
|
63
|
+
maxLength: 120,
|
|
64
|
+
description: 'Idempotency ledger key — retries return the recorded outcome (§6.3).',
|
|
65
|
+
})
|
|
66
|
+
@IsOptional()
|
|
67
|
+
@IsString()
|
|
68
|
+
@MinLength(1)
|
|
69
|
+
@MaxLength(120)
|
|
70
|
+
idempotencyKey?: string;
|
|
71
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class ExportJobResponseDto {
|
|
4
|
+
@ApiProperty({
|
|
5
|
+
example: '0a57fb4a-95c6-4f7e-bd5a-f96dbe0599e3',
|
|
6
|
+
format: 'uuid',
|
|
7
|
+
})
|
|
8
|
+
id!: string;
|
|
9
|
+
|
|
10
|
+
@ApiProperty({
|
|
11
|
+
example: '2c67399d-670c-4025-a5fd-1ea9a211891e',
|
|
12
|
+
format: 'uuid',
|
|
13
|
+
})
|
|
14
|
+
orgId!: string;
|
|
15
|
+
|
|
16
|
+
@ApiProperty({ example: 'abstract-review-board' })
|
|
17
|
+
reportKey!: string;
|
|
18
|
+
|
|
19
|
+
@ApiProperty({ example: 4 })
|
|
20
|
+
reportVersion!: number;
|
|
21
|
+
|
|
22
|
+
@ApiProperty({
|
|
23
|
+
type: 'object',
|
|
24
|
+
additionalProperties: true,
|
|
25
|
+
description: 'The FULL query spec, format, and resolved column list — replayable (finding #8).',
|
|
26
|
+
})
|
|
27
|
+
spec!: Record<string, unknown>;
|
|
28
|
+
|
|
29
|
+
@ApiProperty({
|
|
30
|
+
type: String,
|
|
31
|
+
format: 'date-time',
|
|
32
|
+
nullable: true,
|
|
33
|
+
description: 'Snapshot timestamp, set when the job runs (§9).',
|
|
34
|
+
})
|
|
35
|
+
asOf!: string | null;
|
|
36
|
+
|
|
37
|
+
@ApiProperty({ enum: ['PENDING', 'RUNNING', 'DONE', 'FAILED'], example: 'PENDING' })
|
|
38
|
+
status!: 'PENDING' | 'RUNNING' | 'DONE' | 'FAILED';
|
|
39
|
+
|
|
40
|
+
@ApiProperty({ type: String, format: 'uuid', nullable: true })
|
|
41
|
+
fileId!: string | null;
|
|
42
|
+
|
|
43
|
+
@ApiProperty({ type: Number, nullable: true, example: 52340 })
|
|
44
|
+
rowCount!: number | null;
|
|
45
|
+
|
|
46
|
+
@ApiProperty({ type: String, nullable: true })
|
|
47
|
+
error!: string | null;
|
|
48
|
+
|
|
49
|
+
@ApiProperty({
|
|
50
|
+
example: '4a4f0d8a-4bd2-469f-a6a9-3e1cb6a2b456',
|
|
51
|
+
format: 'uuid',
|
|
52
|
+
})
|
|
53
|
+
requestedBy!: string;
|
|
54
|
+
|
|
55
|
+
@ApiProperty({ example: '2026-06-01T10:30:00.000Z', format: 'date-time' })
|
|
56
|
+
createdAt!: string;
|
|
57
|
+
|
|
58
|
+
@ApiProperty({ example: '2026-06-01T10:30:00.000Z', format: 'date-time' })
|
|
59
|
+
updatedAt!: string;
|
|
60
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { Type } from 'class-transformer';
|
|
3
|
+
import {
|
|
4
|
+
IsArray,
|
|
5
|
+
IsIn,
|
|
6
|
+
IsObject,
|
|
7
|
+
IsOptional,
|
|
8
|
+
IsString,
|
|
9
|
+
ValidateNested,
|
|
10
|
+
} from 'class-validator';
|
|
11
|
+
import { QuerySpecDto } from './query-spec.dto';
|
|
12
|
+
|
|
13
|
+
export class ExportRequestDto {
|
|
14
|
+
@ApiProperty({ enum: ['csv', 'xlsx'], example: 'csv' })
|
|
15
|
+
@IsIn(['csv', 'xlsx'])
|
|
16
|
+
format!: 'csv' | 'xlsx';
|
|
17
|
+
|
|
18
|
+
@ApiPropertyOptional({ type: QuerySpecDto })
|
|
19
|
+
@IsOptional()
|
|
20
|
+
@IsObject()
|
|
21
|
+
@ValidateNested()
|
|
22
|
+
@Type(() => QuerySpecDto)
|
|
23
|
+
spec?: QuerySpecDto;
|
|
24
|
+
|
|
25
|
+
@ApiPropertyOptional({
|
|
26
|
+
type: [String],
|
|
27
|
+
description:
|
|
28
|
+
'Explicit column-id projection. Columns marked exportable:false are stripped regardless (§9).',
|
|
29
|
+
})
|
|
30
|
+
@IsOptional()
|
|
31
|
+
@IsArray()
|
|
32
|
+
@IsString({ each: true })
|
|
33
|
+
columns?: string[];
|
|
34
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { IsIn, IsOptional } from 'class-validator';
|
|
3
|
+
import { PaginationQueryDto } from '../../../common/dto/pagination-query.dto';
|
|
4
|
+
|
|
5
|
+
export class ListReportsQueryDto extends PaginationQueryDto {
|
|
6
|
+
@ApiPropertyOptional({ enum: ['DRAFT', 'PUBLISHED', 'ARCHIVED'] })
|
|
7
|
+
@IsOptional()
|
|
8
|
+
@IsIn(['DRAFT', 'PUBLISHED', 'ARCHIVED'])
|
|
9
|
+
status?: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';
|
|
10
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { Type } from 'class-transformer';
|
|
3
|
+
import { IsInt, IsOptional, Min } from 'class-validator';
|
|
4
|
+
|
|
5
|
+
export class ListViewsQueryDto {
|
|
6
|
+
@ApiPropertyOptional({
|
|
7
|
+
example: 3,
|
|
8
|
+
minimum: 1,
|
|
9
|
+
description:
|
|
10
|
+
'Version the compatibility verdicts are computed against; defaults to the latest published one (§4).',
|
|
11
|
+
})
|
|
12
|
+
@IsOptional()
|
|
13
|
+
@Type(() => Number)
|
|
14
|
+
@IsInt()
|
|
15
|
+
@Min(1)
|
|
16
|
+
version?: number;
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class PrepareActionResponseDto {
|
|
4
|
+
@ApiProperty({ example: 240, description: 'Rows the byFilter selection resolves to right now.' })
|
|
5
|
+
expectedCount!: number;
|
|
6
|
+
|
|
7
|
+
@ApiProperty({ example: '2026-06-01T10:30:00.000Z', format: 'date-time' })
|
|
8
|
+
asOf!: string;
|
|
9
|
+
|
|
10
|
+
@ApiProperty({
|
|
11
|
+
description: 'HMAC-signed, short-TTL token binding report, version, action, and spec (§6.3).',
|
|
12
|
+
})
|
|
13
|
+
actionToken!: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
2
|
+
import { Type } from 'class-transformer';
|
|
3
|
+
import { IsDefined, IsObject, ValidateNested } from 'class-validator';
|
|
4
|
+
import { QuerySpecDto } from './query-spec.dto';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* §6.3 step 1: only byFilter selections prepare — the server resolves the
|
|
8
|
+
* spec NOW and returns a signed token binding what was seen. byIds selections
|
|
9
|
+
* skip prepare entirely (the ids ARE the snapshot) and go straight to execute.
|
|
10
|
+
*/
|
|
11
|
+
export class PrepareActionSelectionDto {
|
|
12
|
+
@ApiProperty({ type: QuerySpecDto })
|
|
13
|
+
@IsDefined()
|
|
14
|
+
@IsObject()
|
|
15
|
+
@ValidateNested()
|
|
16
|
+
@Type(() => QuerySpecDto)
|
|
17
|
+
byFilter!: QuerySpecDto;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class PrepareActionDto {
|
|
21
|
+
@ApiProperty({ type: PrepareActionSelectionDto })
|
|
22
|
+
@IsDefined()
|
|
23
|
+
@IsObject()
|
|
24
|
+
@ValidateNested()
|
|
25
|
+
@Type(() => PrepareActionSelectionDto)
|
|
26
|
+
selection!: PrepareActionSelectionDto;
|
|
27
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class CountResultDto {
|
|
4
|
+
@ApiProperty({
|
|
5
|
+
enum: ['estimated', 'exact', 'capped'],
|
|
6
|
+
description: '"capped" means the true count exceeds the cap — render "value+" (§5.4).',
|
|
7
|
+
})
|
|
8
|
+
mode!: 'estimated' | 'exact' | 'capped';
|
|
9
|
+
|
|
10
|
+
@ApiProperty({ example: 1240 })
|
|
11
|
+
value!: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class PageInfoDto {
|
|
15
|
+
@ApiProperty({
|
|
16
|
+
type: String,
|
|
17
|
+
nullable: true,
|
|
18
|
+
description: 'Opaque, HMAC-signed keyset cursor (§5.3). null on the last page.',
|
|
19
|
+
})
|
|
20
|
+
nextCursor!: string | null;
|
|
21
|
+
|
|
22
|
+
@ApiProperty({ example: true })
|
|
23
|
+
hasMore!: boolean;
|
|
24
|
+
|
|
25
|
+
@ApiPropertyOptional({ type: CountResultDto })
|
|
26
|
+
count?: CountResultDto;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class QueryMetaDto {
|
|
30
|
+
@ApiProperty({ example: 4 })
|
|
31
|
+
reportVersion!: number;
|
|
32
|
+
|
|
33
|
+
@ApiProperty({ type: 'array', items: { type: 'object', additionalProperties: true } })
|
|
34
|
+
appliedFilters!: Record<string, unknown>[];
|
|
35
|
+
|
|
36
|
+
@ApiProperty({ type: 'array', items: { type: 'object', additionalProperties: true } })
|
|
37
|
+
appliedSort!: Record<string, unknown>[];
|
|
38
|
+
|
|
39
|
+
@ApiProperty({
|
|
40
|
+
type: String,
|
|
41
|
+
nullable: true,
|
|
42
|
+
format: 'date-time',
|
|
43
|
+
description: 'Materialized-tier staleness marker; null on live/indexed tiers (§8).',
|
|
44
|
+
})
|
|
45
|
+
freshAsOf!: string | null;
|
|
46
|
+
|
|
47
|
+
@ApiProperty({ example: 14 })
|
|
48
|
+
durationMs!: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class QueryResponseDto {
|
|
52
|
+
@ApiProperty({
|
|
53
|
+
type: 'array',
|
|
54
|
+
items: { type: 'object', additionalProperties: true },
|
|
55
|
+
description: 'Rows: declared column ids plus $id (stable row identity) and $tags (§7).',
|
|
56
|
+
})
|
|
57
|
+
rows!: Record<string, unknown>[];
|
|
58
|
+
|
|
59
|
+
@ApiProperty({ type: PageInfoDto })
|
|
60
|
+
pageInfo!: PageInfoDto;
|
|
61
|
+
|
|
62
|
+
@ApiProperty({ type: QueryMetaDto })
|
|
63
|
+
meta!: QueryMetaDto;
|
|
64
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { Type } from 'class-transformer';
|
|
3
|
+
import {
|
|
4
|
+
IsArray,
|
|
5
|
+
IsDefined,
|
|
6
|
+
IsIn,
|
|
7
|
+
IsInt,
|
|
8
|
+
IsOptional,
|
|
9
|
+
IsString,
|
|
10
|
+
Min,
|
|
11
|
+
ValidateNested,
|
|
12
|
+
} from 'class-validator';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Envelope-only validation of the QuerySpec (report design §4/§10): the DTO
|
|
16
|
+
* checks the shape; the engine's compiler validates the semantics — column
|
|
17
|
+
* ids against the definition's allowlist, operators against column types,
|
|
18
|
+
* cursors against (key, version, sortHash, filterHash). A `contains` on a
|
|
19
|
+
* number column is the engine's 400, not this DTO's.
|
|
20
|
+
*/
|
|
21
|
+
export class FilterClauseDto {
|
|
22
|
+
@ApiProperty({
|
|
23
|
+
example: 'status',
|
|
24
|
+
description: 'A declared column id, or the virtual "$tags" column (§7).',
|
|
25
|
+
})
|
|
26
|
+
@IsString()
|
|
27
|
+
column!: string;
|
|
28
|
+
|
|
29
|
+
@ApiProperty({
|
|
30
|
+
example: 'eq',
|
|
31
|
+
description: 'Filter operator — validated against the column type by the engine.',
|
|
32
|
+
})
|
|
33
|
+
@IsString()
|
|
34
|
+
op!: string;
|
|
35
|
+
|
|
36
|
+
@ApiProperty({
|
|
37
|
+
description: 'Operand — any JSON value; the engine validates it against the column type.',
|
|
38
|
+
})
|
|
39
|
+
@IsDefined()
|
|
40
|
+
value!: unknown;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class SortClauseDto {
|
|
44
|
+
@ApiProperty({ example: 'createdAt' })
|
|
45
|
+
@IsString()
|
|
46
|
+
column!: string;
|
|
47
|
+
|
|
48
|
+
@ApiProperty({ enum: ['asc', 'desc'], example: 'desc' })
|
|
49
|
+
@IsIn(['asc', 'desc'])
|
|
50
|
+
dir!: 'asc' | 'desc';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class QuerySpecDto {
|
|
54
|
+
@ApiPropertyOptional({
|
|
55
|
+
type: Number,
|
|
56
|
+
nullable: true,
|
|
57
|
+
example: null,
|
|
58
|
+
description: 'null/omitted ⇒ latest published; or pin an explicit published version (§4).',
|
|
59
|
+
})
|
|
60
|
+
@IsOptional()
|
|
61
|
+
@IsInt()
|
|
62
|
+
@Min(1)
|
|
63
|
+
version?: number | null;
|
|
64
|
+
|
|
65
|
+
@ApiPropertyOptional({ type: [FilterClauseDto] })
|
|
66
|
+
@IsOptional()
|
|
67
|
+
@IsArray()
|
|
68
|
+
@ValidateNested({ each: true })
|
|
69
|
+
@Type(() => FilterClauseDto)
|
|
70
|
+
filters?: FilterClauseDto[];
|
|
71
|
+
|
|
72
|
+
@ApiPropertyOptional({
|
|
73
|
+
type: String,
|
|
74
|
+
nullable: true,
|
|
75
|
+
example: 'transformer',
|
|
76
|
+
description: "Free-text search across the definition's declared search columns (§5.5).",
|
|
77
|
+
})
|
|
78
|
+
@IsOptional()
|
|
79
|
+
@IsString()
|
|
80
|
+
search?: string | null;
|
|
81
|
+
|
|
82
|
+
@ApiPropertyOptional({ type: [SortClauseDto] })
|
|
83
|
+
@IsOptional()
|
|
84
|
+
@IsArray()
|
|
85
|
+
@ValidateNested({ each: true })
|
|
86
|
+
@Type(() => SortClauseDto)
|
|
87
|
+
sort?: SortClauseDto[];
|
|
88
|
+
|
|
89
|
+
@ApiPropertyOptional({
|
|
90
|
+
type: String,
|
|
91
|
+
nullable: true,
|
|
92
|
+
description: 'Keyset cursor from the previous page, or null for page one (§5.3).',
|
|
93
|
+
})
|
|
94
|
+
@IsOptional()
|
|
95
|
+
@IsString()
|
|
96
|
+
cursor?: string | null;
|
|
97
|
+
|
|
98
|
+
@ApiPropertyOptional({ example: 50, minimum: 1, description: 'Capped server-side by policy.' })
|
|
99
|
+
@IsOptional()
|
|
100
|
+
@IsInt()
|
|
101
|
+
@Min(1)
|
|
102
|
+
pageSize?: number;
|
|
103
|
+
|
|
104
|
+
@ApiPropertyOptional({
|
|
105
|
+
enum: ['none', 'estimated', 'exact-capped'],
|
|
106
|
+
description: 'Counts are a choice, not a tax (§5.4).',
|
|
107
|
+
})
|
|
108
|
+
@IsOptional()
|
|
109
|
+
@IsIn(['none', 'estimated', 'exact-capped'])
|
|
110
|
+
count?: 'none' | 'estimated' | 'exact-capped';
|
|
111
|
+
|
|
112
|
+
@ApiPropertyOptional({
|
|
113
|
+
type: [String],
|
|
114
|
+
description: 'Projection: declared column ids to return. Default: all definition columns.',
|
|
115
|
+
})
|
|
116
|
+
@IsOptional()
|
|
117
|
+
@IsArray()
|
|
118
|
+
@IsString({ each: true })
|
|
119
|
+
columns?: string[];
|
|
120
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class ReportDefinitionResponseDto {
|
|
4
|
+
@ApiProperty({
|
|
5
|
+
example: '0a57fb4a-95c6-4f7e-bd5a-f96dbe0599e3',
|
|
6
|
+
format: 'uuid',
|
|
7
|
+
})
|
|
8
|
+
id!: string;
|
|
9
|
+
|
|
10
|
+
@ApiProperty({
|
|
11
|
+
example: '2c67399d-670c-4025-a5fd-1ea9a211891e',
|
|
12
|
+
format: 'uuid',
|
|
13
|
+
})
|
|
14
|
+
orgId!: string;
|
|
15
|
+
|
|
16
|
+
@ApiProperty({ example: 'abstract-review-board' })
|
|
17
|
+
key!: string;
|
|
18
|
+
|
|
19
|
+
@ApiProperty({ example: 1 })
|
|
20
|
+
version!: number;
|
|
21
|
+
|
|
22
|
+
@ApiProperty({ enum: ['DRAFT', 'PUBLISHED', 'ARCHIVED'], example: 'DRAFT' })
|
|
23
|
+
status!: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';
|
|
24
|
+
|
|
25
|
+
@ApiProperty({
|
|
26
|
+
type: 'object',
|
|
27
|
+
additionalProperties: true,
|
|
28
|
+
description: 'The full ReportDefinition document.',
|
|
29
|
+
})
|
|
30
|
+
schema!: Record<string, unknown>;
|
|
31
|
+
|
|
32
|
+
@ApiPropertyOptional({
|
|
33
|
+
type: 'object',
|
|
34
|
+
additionalProperties: true,
|
|
35
|
+
nullable: true,
|
|
36
|
+
description:
|
|
37
|
+
'Publish-time compiled metadata (logical→physical column map, plan hashes); null on drafts.',
|
|
38
|
+
})
|
|
39
|
+
compiled!: Record<string, unknown> | null;
|
|
40
|
+
|
|
41
|
+
@ApiProperty({ example: '2026-06-01T10:30:00.000Z', format: 'date-time' })
|
|
42
|
+
createdAt!: string;
|
|
43
|
+
|
|
44
|
+
@ApiProperty({ example: '2026-06-01T10:30:00.000Z', format: 'date-time' })
|
|
45
|
+
updatedAt!: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class ReportDefinitionListResponseDto {
|
|
49
|
+
@ApiProperty({ type: [ReportDefinitionResponseDto] })
|
|
50
|
+
items!: ReportDefinitionResponseDto[];
|
|
51
|
+
|
|
52
|
+
@ApiPropertyOptional({
|
|
53
|
+
type: String,
|
|
54
|
+
example: '0a57fb4a-95c6-4f7e-bd5a-f96dbe0599e3',
|
|
55
|
+
format: 'uuid',
|
|
56
|
+
nullable: true,
|
|
57
|
+
})
|
|
58
|
+
nextCursor!: string | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class ReportArchivedResponseDto {
|
|
62
|
+
@ApiProperty({ example: true })
|
|
63
|
+
archived!: boolean;
|
|
64
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { Type } from 'class-transformer';
|
|
3
|
+
import { IsInt, IsOptional, Min } from 'class-validator';
|
|
4
|
+
|
|
5
|
+
export class ReportMetaQueryDto {
|
|
6
|
+
@ApiPropertyOptional({
|
|
7
|
+
example: 3,
|
|
8
|
+
minimum: 1,
|
|
9
|
+
description: 'Specific published version to describe; defaults to the latest published one.',
|
|
10
|
+
})
|
|
11
|
+
@IsOptional()
|
|
12
|
+
@Type(() => Number)
|
|
13
|
+
@IsInt()
|
|
14
|
+
@Min(1)
|
|
15
|
+
version?: number;
|
|
16
|
+
}
|