Skip to content

Commit fa178a7

Browse files
fix(frontend): construct protobuf messages with create(Schema) (#2503)
* fix(frontend): construct protobuf messages with create(Schema) Replace hand-built object literals carrying hardcoded $typeName strings in transcript-list-page (AttributeFilter / ListTracesRequest_Filter) and acl-model (CreateACLRequest) with the protobuf-es v2 create(Schema, ...) API, so a proto rename or package bump can't silently desync the runtime message from its registered schema (audit DATA-01). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(frontend): drop redundant proto-default fields in buildApiFilter Address @claude review nit: create(AttributeFilterSchema) supplies proto defaults, so the explicit value:'' on the IN filter and values:[] on the EQUALS/NOT_EQUALS filters are redundant. Keep the IN filter's real values:['chat','text_completion']. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 73c27bb commit fa178a7

2 files changed

Lines changed: 66 additions & 59 deletions

File tree

frontend/src/components/pages/security/shared/acl-model.tsx

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import { create } from '@bufbuild/protobuf';
12
import type { ConnectError } from '@connectrpc/connect';
23
import {
34
ACL_Operation,
45
ACL_PermissionType,
56
ACL_ResourcePatternType,
67
ACL_ResourceType,
78
type CreateACLRequest,
9+
CreateACLRequestSchema,
810
type ListACLsResponse,
911
} from 'protogen/redpanda/api/dataplane/v1/acl_pb';
1012
import type { ReactNode } from 'react';
@@ -522,45 +524,48 @@ export function getIdFromCreateACLRequest(request: CreateACLRequest): string {
522524
// Convert rules to CreateACLRequest array for API calls
523525
export function convertRulesToCreateACLRequests(rules: Rule[], principal: string, host: string): CreateACLRequest[] {
524526
return rules.reduce((acc, r) => {
525-
const baseRule: Pick<CreateACLRequest, 'resourcePatternType' | '$typeName' | 'resourceName'> = {
527+
const baseRule = {
526528
resourcePatternType:
527529
r.selectorType === 'any' ? ACL_ResourcePatternType.LITERAL : getGRPCResourcePatternType(r.selectorType),
528-
$typeName: 'redpanda.api.dataplane.v1.CreateACLRequest',
529530
resourceName: getResourceNameValue(r),
530531
};
531532
if (r.mode === ModeAllowAll) {
532-
const a: CreateACLRequest = {
533-
...baseRule,
534-
host,
535-
principal,
536-
resourceType: getGRPCResourceType(r.resourceType),
537-
operation: ACL_Operation.ALL,
538-
permissionType: ACL_PermissionType.ALLOW,
539-
};
540-
acc.push(a);
533+
acc.push(
534+
create(CreateACLRequestSchema, {
535+
...baseRule,
536+
host,
537+
principal,
538+
resourceType: getGRPCResourceType(r.resourceType),
539+
operation: ACL_Operation.ALL,
540+
permissionType: ACL_PermissionType.ALLOW,
541+
})
542+
);
541543
}
542544
if (r.mode === ModeDenyAll) {
543-
const a: CreateACLRequest = {
544-
...baseRule,
545-
host,
546-
principal,
547-
resourceType: getGRPCResourceType(r.resourceType),
548-
operation: ACL_Operation.ALL,
549-
permissionType: ACL_PermissionType.DENY,
550-
};
551-
acc.push(a);
545+
acc.push(
546+
create(CreateACLRequestSchema, {
547+
...baseRule,
548+
host,
549+
principal,
550+
resourceType: getGRPCResourceType(r.resourceType),
551+
operation: ACL_Operation.ALL,
552+
permissionType: ACL_PermissionType.DENY,
553+
})
554+
);
552555
}
553556
if (r.mode === ModeCustom) {
554557
const customResults = Object.entries(r.operations).reduce((operations, [op, opValue]) => {
555558
if (opValue !== OperationTypeNotSet) {
556-
operations.push({
557-
...baseRule,
558-
host,
559-
principal,
560-
resourceType: getGRPCResourceType(r.resourceType),
561-
operation: getGRPCOperationType(op),
562-
permissionType: getGRPCPermissionType(opValue),
563-
});
559+
operations.push(
560+
create(CreateACLRequestSchema, {
561+
...baseRule,
562+
host,
563+
principal,
564+
resourceType: getGRPCResourceType(r.resourceType),
565+
operation: getGRPCOperationType(op),
566+
permissionType: getGRPCPermissionType(opValue),
567+
})
568+
);
564569
}
565570
return operations;
566571
}, [] as CreateACLRequest[]);

frontend/src/components/pages/transcripts/transcript-list-page.tsx

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* by the Apache License, Version 2.0
1010
*/
1111

12+
import { create } from '@bufbuild/protobuf';
1213
import { timestampFromMs } from '@bufbuild/protobuf/wkt';
1314
import type { ColumnFiltersState } from '@tanstack/react-table';
1415
import { Button } from 'components/redpanda-ui/components/button';
@@ -17,8 +18,10 @@ import { Heading, Link, Text } from 'components/redpanda-ui/components/typograph
1718
import { parseAsArrayOf, parseAsBoolean, parseAsString, useQueryStates } from 'nuqs';
1819
import {
1920
type AttributeFilter,
21+
AttributeFilterSchema,
2022
AttributeOperator,
2123
type ListTracesRequest_Filter,
24+
ListTracesRequest_FilterSchema,
2225
type TraceSummary,
2326
} from 'protogen/redpanda/api/dataplane/v1alpha3/tracing_pb';
2427
import type { FC } from 'react';
@@ -214,50 +217,49 @@ const buildApiFilter = ({
214217

215218
// LLM filter → attribute filter with IN operator
216219
if (activePresets.includes('llm')) {
217-
attributeFilters.push({
218-
$typeName: 'redpanda.api.dataplane.v1alpha3.AttributeFilter',
219-
key: 'gen_ai.operation.name',
220-
operator: AttributeOperator.IN,
221-
value: '',
222-
values: ['chat', 'text_completion'],
223-
});
220+
attributeFilters.push(
221+
create(AttributeFilterSchema, {
222+
key: 'gen_ai.operation.name',
223+
operator: AttributeOperator.IN,
224+
values: ['chat', 'text_completion'],
225+
})
226+
);
224227
}
225228

226229
// Tool filter → attribute filter
227230
if (activePresets.includes('tool')) {
228-
attributeFilters.push({
229-
$typeName: 'redpanda.api.dataplane.v1alpha3.AttributeFilter',
230-
key: 'gen_ai.operation.name',
231-
operator: AttributeOperator.EQUALS,
232-
value: 'execute_tool',
233-
values: [],
234-
});
231+
attributeFilters.push(
232+
create(AttributeFilterSchema, {
233+
key: 'gen_ai.operation.name',
234+
operator: AttributeOperator.EQUALS,
235+
value: 'execute_tool',
236+
})
237+
);
235238
}
236239

237240
// Agent filter → attribute filter
238241
if (activePresets.includes('agent')) {
239-
attributeFilters.push({
240-
$typeName: 'redpanda.api.dataplane.v1alpha3.AttributeFilter',
241-
key: 'gen_ai.operation.name',
242-
operator: AttributeOperator.EQUALS,
243-
value: 'invoke_agent',
244-
values: [],
245-
});
242+
attributeFilters.push(
243+
create(AttributeFilterSchema, {
244+
key: 'gen_ai.operation.name',
245+
operator: AttributeOperator.EQUALS,
246+
value: 'invoke_agent',
247+
})
248+
);
246249
}
247250

248251
// Add user-defined attribute filters
249252
for (const f of urlAttrFilters) {
250-
attributeFilters.push({
251-
$typeName: 'redpanda.api.dataplane.v1alpha3.AttributeFilter',
252-
key: f.key,
253-
operator: f.op === 'equals' ? AttributeOperator.EQUALS : AttributeOperator.NOT_EQUALS,
254-
value: f.value,
255-
values: [],
256-
});
253+
attributeFilters.push(
254+
create(AttributeFilterSchema, {
255+
key: f.key,
256+
operator: f.op === 'equals' ? AttributeOperator.EQUALS : AttributeOperator.NOT_EQUALS,
257+
value: f.value,
258+
})
259+
);
257260
}
258261

259-
return {
260-
$typeName: 'redpanda.api.dataplane.v1alpha3.ListTracesRequest.Filter',
262+
return create(ListTracesRequest_FilterSchema, {
261263
startTime: startTimestamp,
262264
endTime: endTimestamp,
263265
attributeFilters,
@@ -268,7 +270,7 @@ const buildApiFilter = ({
268270
serviceNames: serviceNames.length > 0 ? serviceNames : [],
269271
// Span ID filter - not exposed in UI yet, but required by proto
270272
spanIds: [],
271-
};
273+
});
272274
};
273275

274276
type TranscriptListPageProps = {

0 commit comments

Comments
 (0)