| 1 |
|
package graph |
| 2 |
|
|
| 3 |
|
import ( |
| 4 |
|
"context" |
| 5 |
|
|
| 6 |
|
"sourcecraft.dev/bigbes/sr-ht-spec/core" |
| 7 |
|
"sourcecraft.dev/bigbes/sr-ht-spec/service" |
| 8 |
|
) |
| 9 |
|
|
| 10 |
|
// proposalLister is the service-layer proposal read this package adapts. It is |
| 11 |
|
// the method *service.Service grew in Phase 3, named as an interface here so the |
| 12 |
|
// adapter is testable without a whole Service and so this file states exactly |
| 13 |
|
// what it depends on. |
| 14 |
|
type proposalLister interface { |
| 15 |
|
ListProposals(ctx context.Context, space core.SpaceRef, state core.ProposalState) ([]service.Proposal, error) |
| 16 |
|
} |
| 17 |
|
|
| 18 |
|
// serviceProposals adapts the service layer to the Proposals port. |
| 19 |
|
// |
| 20 |
|
// The port yields graph.Proposal (core types, no db/ leak) while the service |
| 21 |
|
// yields service.Proposal, and the two cannot be one type without a dependency |
| 22 |
|
// cycle — service/ must not import graph/. So the mapping lives here, at the |
| 23 |
|
// edge, exactly as web.NewReader adapts the same service to the web surface's |
| 24 |
|
// read shape. It is a field-for-field copy; the two structs are deliberately |
| 25 |
|
// identical so this stays a rename rather than a translation. |
| 26 |
|
type serviceProposals struct{ svc proposalLister } |
| 27 |
|
|
| 28 |
|
// NewProposals wires the service layer into the schema's proposals field, |
| 29 |
|
// closing the gap the Proposals port documented: with this set, Options.Proposals |
| 30 |
|
// is no longer nil and the `proposals` query answers from service/ instead of |
| 31 |
|
// failing with "no proposal listing yet". |
| 32 |
1 |
func NewProposals(svc proposalLister) Proposals { return serviceProposals{svc: svc} } |
| 33 |
|
|
| 34 |
1 |
func (s serviceProposals) ListProposals(ctx context.Context, space core.SpaceRef, state core.ProposalState) ([]Proposal, error) { |
| 35 |
1 |
ps, err := s.svc.ListProposals(ctx, space, state) |
| 36 |
1 |
if err != nil { |
| 37 |
0 |
return nil, err |
| 38 |
0 |
} |
| 39 |
1 |
out := make([]Proposal, 0, len(ps)) |
| 40 |
1 |
for _, p := range ps { |
| 41 |
1 |
out = append(out, Proposal{ |
| 42 |
1 |
ID: p.ID, |
| 43 |
1 |
Space: p.Space, |
| 44 |
1 |
Title: p.Title, |
| 45 |
1 |
Rationale: p.Rationale, |
| 46 |
1 |
BaseRev: p.BaseRev, |
| 47 |
1 |
Branch: p.Branch, |
| 48 |
1 |
State: p.State, |
| 49 |
1 |
Approval: p.Approval, |
| 50 |
1 |
MergedRev: p.MergedRev, |
| 51 |
1 |
Agent: p.Agent, |
| 52 |
1 |
AgentSession: p.AgentSession, |
| 53 |
1 |
Created: p.Created, |
| 54 |
1 |
Resolved: p.Resolved, |
| 55 |
1 |
}) |
| 56 |
1 |
} |
| 57 |
1 |
return out, nil |
| 58 |
|
} |
| 59 |
|
|
| 60 |
|
// Compile-time assertion that the production service satisfies the read this |
| 61 |
|
// adapter needs. It lives here, beside the assertions in resolver.go, so a |
| 62 |
|
// signature drift in service/ breaks the build rather than a test. |
| 63 |
|
var _ proposalLister = (*service.Service)(nil) |