Added api/classifications endpoint, requiring a new query
This commit is contained in:
1 parent
378ac59f41
commit
0e27d2b2f3
9 files changed
+186
-6
No files matched your search
@@ -75,7 +75,9 @@ SELECT t.* FROM opportunities t WHERE t.uuid = ?`
|
||||
|
||||
// ListOpportunities is assembled by listOpportunitiesQuery as:
|
||||
//
|
||||
// listOpportunitiesSelect [WHERE filter AND filter ...] listOpportunitiesOrderAndPage
|
||||
// listOpportunitiesSelect
|
||||
// [WHERE filter AND filter ...]
|
||||
// listOpportunitiesOrderAndPage
|
||||
//
|
||||
// Each filterOpportunity* fragment takes exactly one "?" argument, except
|
||||
// filterOpportunityMark* fragments, which take none. A mark fragment is true
|
||||
@@ -213,6 +215,17 @@ INSERT INTO opportunity_classifications (opportunity_uuid, classification_id)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT DO NOTHING`
|
||||
|
||||
// selectClassificationsInUse reads every classification code that at least
|
||||
// one opportunity has, once each. c.* must map entirely onto
|
||||
// types.ClassificationCode.
|
||||
const selectClassificationsInUse = `
|
||||
SELECT c.* FROM classification_codes c
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM opportunity_classifications tc
|
||||
WHERE tc.classification_id = c.id
|
||||
)
|
||||
ORDER BY c.code, c.id`
|
||||
|
||||
// selectClassificationsForOpportunities reads the classifications of several
|
||||
// opportunities. "IN (?)" is expanded by sqlx.In, which must be followed by
|
||||
// Rebind. c.* must map entirely onto types.ClassificationCode.
|
||||
|
||||
@@ -100,6 +100,9 @@ var checks = map[string]queryCheck{
|
||||
"insertOpportunityClassification": {
|
||||
sql: insertOpportunityClassification,
|
||||
},
|
||||
"selectClassificationsInUse": {
|
||||
sql: selectClassificationsInUse,
|
||||
},
|
||||
"selectClassificationsForOpportunities": {
|
||||
sql: selectClassificationsForOpportunities,
|
||||
in: true,
|
||||
|
||||
@@ -717,6 +717,21 @@ func (s *Store) ListReleases(
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
// ListClassifications returns every classification code that at least one
|
||||
// stored opportunity has, ordered by code. A code no opportunity has any more
|
||||
// isn't listed. The result is never nil.
|
||||
func (s *Store) ListClassifications(
|
||||
ctx context.Context,
|
||||
) ([]types.ClassificationCode, error) {
|
||||
classifications := []types.ClassificationCode{}
|
||||
err := s.db.SelectContext(ctx, &classifications, selectClassificationsInUse)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing classifications: %w", err)
|
||||
}
|
||||
|
||||
return classifications, nil
|
||||
}
|
||||
|
||||
// LatestReleaseDate returns the date of the newest release stored for the
|
||||
// opportunities from source, or the zero time if none are stored, so a
|
||||
// fetcher can carry on from where it left off.
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -902,3 +903,44 @@ func TestSaveResultNewOpportunity(t *testing.T) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ListClassifications lists the codes in use, once each and sorted, and not
|
||||
// a code no opportunity has any more.
|
||||
func TestListClassifications(t *testing.T) {
|
||||
s := openStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
got, err := s.ListClassifications(ctx)
|
||||
if err != nil || got == nil || len(got) != 0 {
|
||||
t.Fatalf("empty store: %v, %v; want an empty, non-nil list", got, err)
|
||||
}
|
||||
|
||||
mustSave(t, s, opportunity("ocds-1", "First", 1, "r1"))
|
||||
mustSave(t, s, opportunity("ocds-2", "Second", 1, "r1"))
|
||||
codes := func() []string {
|
||||
t.Helper()
|
||||
got, err := s.ListClassifications(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var listed []string
|
||||
for _, classification := range got {
|
||||
listed = append(listed, classification.Code)
|
||||
}
|
||||
return listed
|
||||
}
|
||||
if got := strings.Join(codes(), ","); got != "03000000,72000000" {
|
||||
t.Fatalf("codes = %s; want 03000000,72000000", got)
|
||||
}
|
||||
|
||||
// Newer releases replace both opportunities' classifications with one
|
||||
// code; the other is still stored, but no longer listed.
|
||||
for _, uuid := range []string{"ocds-1", "ocds-2"} {
|
||||
newer := opportunity(uuid, "Newer", 2, "r2")
|
||||
newer.Classifications = newer.Classifications[1:]
|
||||
mustSave(t, s, newer)
|
||||
}
|
||||
if got := strings.Join(codes(), ","); got != "03000000" {
|
||||
t.Errorf("after the change: codes = %s; want 03000000", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user