Skip to content

Commit caeef62

Browse files
fix(review): address PR #126 review feedback (iteration 2)
Address review comments from @dealako, @prabodhcs, coderabbitai[bot], copilot[bot]: - committee_service.go: reject a missing Heimdall principal with 400 before the writer is called, rather than persisting an empty last_edited_by — matches the sibling write handlers (CreateCommitteeLink, etc.) (per @prabodhcs, @dealako, coderabbitai[bot]). - committee_service.go: move the nil-writer guard ahead of GetBase so a misconfigured service fails fast without a wasted storage read (per @dealako, @prabodhcs). - group_weekly_brief_writer.go: reject revision==0 as a 400 before the revision comparison — a persisted brief always has a KV revision >= 1, so a 0 token is a malformed/missing client value, not a conflict (per copilot[bot], @prabodhcs). - design + regen: add dsl.Minimum(1) to the request and response revision attributes so an invalid token is rejected as 400 at the transport layer and the >=1 invariant is reflected in the schema (per copilot[bot]). - group_weekly_brief_writer.go: normalize a zero Now to time.Now().UTC(), matching the generator, so an omitted Now uses the live window (per copilot[bot]). - group_weekly_brief_writer.go: document why the option constructors carry the ForWriter suffix (package-level name collision with the reader/ generator options; mirrors the generator's ForGenerator) (per @dealako). - tests: add handler missing-principal -> 400; writer CAS-conflict + re-read-fails -> propagate original; writer revision==0 -> 400 (per @dealako). Resolves 11 review threads; 1 (index-write-after-CAS edge) tracked as a follow-up. LFXV2-2194 Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
1 parent be079f1 commit caeef62

13 files changed

Lines changed: 139 additions & 14 deletions

File tree

cmd/committee-api/design/committee.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,7 @@ var _ = dsl.Service("committee-service", func() {
15641564
dsl.Example("## This week\n\n- Shipped the thing.")
15651565
})
15661566
dsl.Attribute("revision", dsl.UInt64, "Optimistic-concurrency token from the brief being edited (GET /current)", func() {
1567+
dsl.Minimum(1)
15671568
dsl.Example(uint64(7))
15681569
})
15691570

cmd/committee-api/design/type.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,7 @@ var GroupWeeklyBriefWithReadonlyAttributes = dsl.Type("group-weekly-brief-with-r
11331133
dsl.Example("jsmith")
11341134
})
11351135
dsl.Attribute("revision", dsl.UInt64, "Optimistic-concurrency token. Echo this back in PUT /current; a stale value yields 409.", func() {
1136+
dsl.Minimum(1)
11361137
dsl.Example(uint64(7))
11371138
})
11381139
})

cmd/committee-api/service/committee_service.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,6 +1785,11 @@ func (s *committeeServicesrvc) UpdateCurrentWeeklyBrief(ctx context.Context, p *
17851785
// Authorization (committee writer relation) is enforced at the edge by
17861786
// Heimdall before the request reaches this service; no in-code check here.
17871787

1788+
// Fail fast on misconfiguration before any storage I/O.
1789+
if s.weeklyBriefWriter == nil {
1790+
return nil, wrapError(ctx, errors.NewServiceUnavailable("weekly brief writer is not configured"))
1791+
}
1792+
17881793
// Verify the committee exists so a typo'd UID returns 404 for the committee
17891794
// rather than 404 for a missing brief.
17901795
base, _, err := s.committeeReaderOrchestrator.GetBase(ctx, p.UID)
@@ -1795,13 +1800,14 @@ func (s *committeeServicesrvc) UpdateCurrentWeeklyBrief(ctx context.Context, p *
17951800
return nil, wrapError(ctx, errors.NewNotFound("committee not found"))
17961801
}
17971802

1798-
if s.weeklyBriefWriter == nil {
1799-
return nil, wrapError(ctx, errors.NewServiceUnavailable("weekly brief writer is not configured"))
1800-
}
1801-
18021803
// PrincipalContextID is the caller's LFX username (Heimdall principal claim),
1803-
// recorded as last_edited_by.
1804+
// recorded as last_edited_by. Reject a missing principal rather than persist
1805+
// an empty editor — this endpoint's audit trail depends on it, and it mirrors
1806+
// the guard the sibling write handlers use (CreateCommitteeLink, etc.).
18041807
editedBy, _ := ctx.Value(constants.PrincipalContextID).(string)
1808+
if editedBy == "" {
1809+
return nil, wrapError(ctx, errors.NewValidation("unable to determine user identity from token"))
1810+
}
18051811

18061812
updated, err := s.weeklyBriefWriter.Update(ctx, service.GroupWeeklyBriefUpdateInput{
18071813
CommitteeUID: p.UID,

cmd/committee-api/service/group_weekly_brief_test.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ func TestUpdateCurrentWeeklyBrief_RevisionConflict(t *testing.T) {
440440
writer := &stubGroupWeeklyBriefWriter{err: errors.NewRevisionMismatch(8)}
441441
svc := newUpdateSvc(&model.CommitteeBase{}, writer)
442442

443-
res, err := svc.UpdateCurrentWeeklyBrief(context.Background(), &committeeservice.UpdateCurrentWeeklyBriefPayload{
443+
ctx := context.WithValue(context.Background(), constants.PrincipalContextID, "alice")
444+
res, err := svc.UpdateCurrentWeeklyBrief(ctx, &committeeservice.UpdateCurrentWeeklyBriefPayload{
444445
UID: "c-1", BriefText: "x", Revision: 7,
445446
})
446447
require.Error(t, err)
@@ -456,7 +457,8 @@ func TestUpdateCurrentWeeklyBrief_BriefNotFound(t *testing.T) {
456457
writer := &stubGroupWeeklyBriefWriter{err: errors.NewNotFound("no weekly brief exists for the current window")}
457458
svc := newUpdateSvc(&model.CommitteeBase{}, writer)
458459

459-
res, err := svc.UpdateCurrentWeeklyBrief(context.Background(), &committeeservice.UpdateCurrentWeeklyBriefPayload{
460+
ctx := context.WithValue(context.Background(), constants.PrincipalContextID, "alice")
461+
res, err := svc.UpdateCurrentWeeklyBrief(ctx, &committeeservice.UpdateCurrentWeeklyBriefPayload{
460462
UID: "c-1", BriefText: "x", Revision: 1,
461463
})
462464
require.Error(t, err)
@@ -470,7 +472,8 @@ func TestUpdateCurrentWeeklyBrief_EmptyBriefTextBadRequest(t *testing.T) {
470472
writer := &stubGroupWeeklyBriefWriter{err: errors.NewValidation("brief_text is required")}
471473
svc := newUpdateSvc(&model.CommitteeBase{}, writer)
472474

473-
res, err := svc.UpdateCurrentWeeklyBrief(context.Background(), &committeeservice.UpdateCurrentWeeklyBriefPayload{
475+
ctx := context.WithValue(context.Background(), constants.PrincipalContextID, "alice")
476+
res, err := svc.UpdateCurrentWeeklyBrief(ctx, &committeeservice.UpdateCurrentWeeklyBriefPayload{
474477
UID: "c-1", BriefText: "", Revision: 1,
475478
})
476479
require.Error(t, err)
@@ -479,6 +482,23 @@ func TestUpdateCurrentWeeklyBrief_EmptyBriefTextBadRequest(t *testing.T) {
479482
require.ErrorAs(t, err, &br)
480483
}
481484

485+
func TestUpdateCurrentWeeklyBrief_MissingPrincipalBadRequest(t *testing.T) {
486+
// No principal in context (e.g. a misconfigured edge) must not persist an
487+
// empty last_edited_by — the handler rejects it with 400 before calling the
488+
// writer, matching the sibling write handlers (CreateCommitteeLink, etc.).
489+
writer := &stubGroupWeeklyBriefWriter{}
490+
svc := newUpdateSvc(&model.CommitteeBase{}, writer)
491+
492+
res, err := svc.UpdateCurrentWeeklyBrief(context.Background(), &committeeservice.UpdateCurrentWeeklyBriefPayload{
493+
UID: "c-1", BriefText: "x", Revision: 1,
494+
})
495+
require.Error(t, err)
496+
assert.Nil(t, res)
497+
var br *committeeservice.BadRequestError
498+
require.ErrorAs(t, err, &br)
499+
assert.False(t, writer.called, "writer must not be called when the principal is missing")
500+
}
501+
482502
func TestUpdateCurrentWeeklyBrief_WriterNotConfigured(t *testing.T) {
483503
// A nil writer is a misconfiguration → 503.
484504
svc := &committeeServicesrvc{

gen/http/committee_service/client/cli.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/http/committee_service/client/types.go

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/http/committee_service/server/types.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/http/openapi.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

gen/http/openapi.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4558,6 +4558,7 @@ definitions:
45584558
description: Optimistic-concurrency token from the brief being edited (GET /current)
45594559
example: 7
45604560
format: int64
4561+
minimum: 1
45614562
example:
45624563
brief_text: |-
45634564
## This week
@@ -5162,6 +5163,7 @@ definitions:
51625163
description: Optimistic-concurrency token. Echo this back in PUT /current; a stale value yields 409.
51635164
example: 7
51645165
format: int64
5166+
minimum: 1
51655167
source_refs:
51665168
type: array
51675169
items:

gen/http/openapi3.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)