feat(billing): add BulkUpdateBillingGroup RPC for bulk invoice settings update - #892
Open
quantdale wants to merge 2 commits into
Open
feat(billing): add BulkUpdateBillingGroup RPC for bulk invoice settings update#892quantdale wants to merge 2 commits into
quantdale wants to merge 2 commits into
Conversation
…gs update Additive contract change: - Billing.BulkUpdateBillingGroup, POST /v1/billinggroups:bulkUpdate, long-running operation (protos.Operation) - Request: csvContent, delimiter, hasHeader, vendor, dryRun - Per-row results (BulkBillingGroupInvoiceSettingsRow): canonical CSV row index, billing group ID, status, errors, disposition, csvLineNumber, normalized settings with per-field source classification - NormalizedInvoiceSettings extended additively to 22 fields (fields 1-8 retain numbers; 9-22 added) - Blank Update cells preserve existing values; missing billing group fails per row; dry-run shares normalization with execution - BulkCreateBillingGroup shapes unchanged - openapiv2/apidocs.swagger.json regenerated (additive delta only) OpenSpec: alphauslabs/ripple-openspec PR #82 (add-billing-group-bulk-invoice-settings-update)
…tual semantics Reviewer-proofing only — no wire/message/field changes: - hasHeader: document the ACTUAL semantics (true = first line names columns; false OR omitted = canonical bulk-update column order, headerless; callers SHOULD set it explicitly; value preserved on the async payload; contrast with BulkCreateBillingGroup which always treats the first line as header). The previous 'Defaults to true' comment did not match the producer. - row disposition: document the full producer vocabulary including 'connection_resolution_failed' and 'unsupported_field'. - error reason: add 'unsupported_field'; align index comment with the canonical row-index definition (zero-based, header excluded, never compressed) shared with BulkBillingGroupInvoiceSettingsRow.index. Verified: buf build OK; descriptor-level buf breaking vs origin/main clean (FILE rules, zero findings); descriptor-set diff vs main shows only 6 added messages, 1 added method, 1 additive repeated field (BulkCreateBillingGroupMetadata.rows=9); SDKs regenerated and validated from this head (Go build+test ./... green, TS tsc+pack green with 22/22 normalized fields, Python compile/import clean — with stubs for two pre-existing broken imports identical on current main).
Author
|
Corrective review pass complete — head updated to db01822 (CI green, re-requesting your review). No wire/message/field changes were made; this pass was reviewer-proofing only:
The description's compatibility section now states precisely: existing BulkCreateBillingGroup fields/numbers/RPC/route/semantics unchanged, with one additive repeated metadata field ( |
tituscarl
approved these changes
Sep 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Feature
Ripple Bulk Invoice Settings + Update — new RPC
Billing.BulkUpdateBillingGroup.API route
POST /v1/billinggroups:bulkUpdate→ long-running operation (protos.Operation); poll viaGET /ops/v1/{name}Contract
csvContent,delimiter,hasHeader,vendor(aws/azure/gcp),dryRunhasHeadersemantics (matches the actual producer, mobingilabs/ouchanParseCSVForBulkUpdate): true → the first CSV line names the columns and columns are matched by name; false or omitted → the CSV is interpreted in the canonical bulk-update column order (the downloadable template, headerless). Callers SHOULD set it explicitly (the Ripple UI always does). UnlikeBulkCreateBillingGroup— which always treats the first CSV line as a header — Bulk Update honors this field as given, and the value is preserved on the async execution payload. (Plainbool, field number 3, mirroring the sibling Bulk Create request; proto3optionalwas deliberately NOT used — see "Contract decisions" below.)BulkBillingGroupInvoiceSettingsRow): canonical logical CSV row index (zero-based, header excluded whenhasHeaderis true, never compressed after malformed/missing rows), user-facing billing-group ID,status(valid/invalid/not_found/updated/failed;createdreserved for the Create side), errors, explicitdisposition(ready/invalid/not_found/connection_resolution_failed/unsupported_field/updated/failed — documented to exactly match the producer vocabulary), physicalcsvLineNumber, normalized Invoice Settings with per-field source classification (uploaded/existing/default_derived/org_derived/connection_resolved)BulkUpdateBillingGroupError.reason: not_found / validation_failed / connection_resolution_failed / unsupported_field;indexuses the same canonical row-index definition as row resultindexNormalizedInvoiceSettings: 22 fields (calcType..serviceDiscounts keep numbers 1-8; currency..supportFeeAdjustment added as 9-22)Compatibility statement
Wire-level compatibility proven by tooling, not line counts:
buf breaking --against origin/main(FILE rules, submodule-resolved against-ref) — zero findingsBulkCreateBillingGroupMetadatareceives ONE new additive repeated field (rows = 9,BulkBillingGroupInvoiceSettingsRow) so Create and Update share one normalized row-result shape (per the OpenSpec design decision D2 and the shared preview contract; the Ripple UI consumes this field typed-optional). Older protobuf clients ignore the unknown field; no existing field or accessor changes. No producer populates it until the Create-side rollout — it is additive and optional on the wire.vendor/csvContentare documented "Required." in comments (mirroringBulkCreateBillingGroupRequestexactly).google.api.field_behaviorannotations were deliberately NOT added: the bulk-RPC request family (BulkCreate/Tag/Link) consistently omits them and enforces requiredness server-side; annotating only the new sibling would create swagger asymmetry with Bulk Create.Contract decisions (reviewer-relevant)
hasHeaderkept as plainbool(field 3, same as Bulk Create) with truthful comments instead ofoptional bool: converting to proto3optionalwould change the generated API surface of a brand-new message family mid-review, require presence-threading through the async PubSub payload in the consumer (ouchan), and diverge from the sibling request it intentionally mirrors. The comment now documents omitted=false behavior unambiguously.BulkCreateBillingGroupMetadata.rows = 9kept: required by the shared normalized-preview contract (OpenSpecbilling-group-bulk-invoice-settings-previewcovers the Create-bulk page; the UI consumesmetadata.rowsfor Create dry-runs too). Wire-additive only.SDK regeneration impact
On merge to main, CI (
.github/workflows/main.yml) regenerates and pushesblue-sdk-go,blue-sdk-ts,blue-sdk-python,blueapidocs. Locally reproduced from this exact head (db01822) with CI tool versions:go test ./...all pass; symbolsBulkUpdateBillingGroup,BulkUpdateBillingGroupRequest,BulkUpdateBillingGroupMetadata,BulkBillingGroupInvoiceSettingsRow,NormalizedInvoiceSettingspresent (incl. gateway route)tscbuild clean;npm packtarball verified to containbulkUpdateBillingGroup+ all 22 normalized fields + all result/metadata typescompileall+ import/descriptor check clean (two pre-existing broken imports —protos,protoc_gen_openapiv2— exist identically on current main; not introduced by this PR)Swagger
openapiv2/apidocs.swagger.jsonregenerated (protoc-gen-openapiv2): purely additive vs origin/main — new route/v1/billinggroups:bulkUpdate(operationIdBilling_BulkUpdateBillingGroup) + new request definition; the only changed line vs the previous commit is the corrected hasHeader description. No unrelated churn.Validation
buf build— OKbuf breaking --against origin/main(with submodule-resolved against worktree; lint-rule workaround for the pre-existing FIELD_NO_DESCRIPTOR config issue) — zero findingsbuf lint— fails identically on cleanorigin/main("FIELD_NO_DESCRIPTOR" is not a known rule or category ID); pre-existing config/toolchain mismatch, not a CI gateOpenSpec
alphauslabs/ripple-openspec#82 —
add-billing-group-bulk-invoice-settings-update. No OpenSpec change required: the spec intentionally leaves the string vocabulary to the API contract and makes no default-true hasHeader claim; this PR only documents the already-specified behavior accurately.Safety
No production mutations. No SDK publication from this PR. Deployment/publication happens only through the existing main-branch CI pipeline after human merge.