Skip to content

Commit

Permalink
Add time range to tenants (#120)
Browse files Browse the repository at this point in the history
Introduces a time range for the source/target links of a tenant.
  • Loading branch information
anothertobi authored Mar 3, 2023
1 parent cc2c7c7 commit 562ae0e
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 9 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ createdb --username=reporting -h localhost -p 5432 appuio-cloud-reporting-test

export ACR_DB_URL="postgres://reporting:reporting@localhost/appuio-cloud-reporting-test?sslmode=disable"

# Required for tests
make ensure-prometheus

go run . migrate
go run . migrate --seed
go test ./...
Expand All @@ -126,7 +129,7 @@ psql -U reporting -W -h localhost appuio-cloud-reporting-test

To enable IDE Test/Debug support, `ACR_DB_URL` should be added to the test environment.

#### VSCode
#### VS Code

```sh
mkdir -p .vscode
Expand Down
8 changes: 8 additions & 0 deletions pkg/db/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ func (c Category) ForeignKeyName() string {
return "category_id"
}

// CreateTenant creates the given tenant
func CreateTenant(p NamedPreparer, in Tenant) (Tenant, error) {
var tenant Tenant
err := GetNamed(p, &tenant,
"INSERT INTO tenants (source,target,during) VALUES (:source,:target,:during) RETURNING *", in)
return tenant, err
}

var _ Model = Product{}

type Product struct {
Expand Down
75 changes: 75 additions & 0 deletions pkg/invoice/invoice_golden_tenants_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package invoice_test

import (
"database/sql"
"time"

"github.com/appuio/appuio-cloud-reporting/pkg/db"
"github.com/stretchr/testify/require"
)

func (s *InvoiceGoldenSuite) TestInvoiceGolden_Tenants() {
t := s.T()
tdb := s.DB()

_, err := db.CreateTenant(tdb, db.Tenant{
Source: "tricell",
Target: sql.NullString{Valid: true, String: "98757"},
During: timerange(t, "-", "2022-01-20"),
})
require.NoError(t, err)

_, err = db.CreateTenant(tdb, db.Tenant{
Source: "tricell",
Target: sql.NullString{Valid: true, String: "98942"},
During: timerange(t, "2022-01-20", "-"),
})
require.NoError(t, err)

_, err = db.CreateTenant(tdb, db.Tenant{
Source: "umbrellacorp",
Target: sql.NullString{Valid: true, String: "96432"},
During: db.InfiniteRange(),
})
require.NoError(t, err)

_, err = db.CreateTenant(tdb, db.Tenant{
Source: "megacorp",
Target: sql.NullString{Valid: true, String: "83492"},
During: db.InfiniteRange(),
})
require.NoError(t, err)

_, err = db.CreateProduct(tdb, db.Product{
Source: "my-product",
Amount: 1,
During: db.InfiniteRange(),
})
require.NoError(t, err)

_, err = db.CreateDiscount(tdb, db.Discount{
Source: "my-product",
During: db.InfiniteRange(),
})
require.NoError(t, err)

query, err := db.CreateQuery(tdb, db.Query{
Name: "test",
Description: "test description",
Query: "test",
Unit: "tps",
During: db.InfiniteRange(),
})
require.NoError(t, err)

s.prom.queries[query.Query] = fakeQueryResults{
"my-product:my-cluster:tricell:my-namespace": fakeQuerySample{Value: 42}, // split over two tenant targets
"my-product:my-cluster:megacorp:my-namespace": fakeQuerySample{Value: 42}, // same value to verify that the sum of both tricell tenant targets is correct
"my-product:my-cluster:umbrellacorp:my-namespace": fakeQuerySample{Value: 14},
}

runReport(t, tdb, s.prom, query.Query, "2022-01-01", "2022-01-30")
invoiceEqualsGolden(t, "tenants",
generateInvoice(t, tdb, 2022, time.January),
*updateGolden)
}
134 changes: 134 additions & 0 deletions pkg/invoice/testdata/tenants.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
[
{
"Tenant": {
"Source": "megacorp",
"Target": "83492"
},
"PeriodStart": "2022-01-01T00:00:00Z",
"PeriodEnd": "2022-01-31T00:00:00Z",
"Categories": [
{
"Source": "my-cluster:my-namespace",
"Target": "",
"Items": [
{
"Description": "test description",
"QueryName": "test",
"Source": "my-product",
"Target": "",
"Quantity": 29232,
"QuantityMin": 42,
"QuantityAvg": 42,
"QuantityMax": 42,
"Unit": "tps",
"PricePerUnit": 1,
"Discount": 0,
"Total": 29232,
"SubItems": {}
}
],
"Total": 29232
}
],
"Total": 29232
},
{
"Tenant": {
"Source": "tricell",
"Target": "98757"
},
"PeriodStart": "2022-01-01T00:00:00Z",
"PeriodEnd": "2022-01-31T00:00:00Z",
"Categories": [
{
"Source": "my-cluster:my-namespace",
"Target": "",
"Items": [
{
"Description": "test description",
"QueryName": "test",
"Source": "my-product",
"Target": "",
"Quantity": 19152,
"QuantityMin": 42,
"QuantityAvg": 42,
"QuantityMax": 42,
"Unit": "tps",
"PricePerUnit": 1,
"Discount": 0,
"Total": 19152,
"SubItems": {}
}
],
"Total": 19152
}
],
"Total": 19152
},
{
"Tenant": {
"Source": "tricell",
"Target": "98942"
},
"PeriodStart": "2022-01-01T00:00:00Z",
"PeriodEnd": "2022-01-31T00:00:00Z",
"Categories": [
{
"Source": "my-cluster:my-namespace",
"Target": "",
"Items": [
{
"Description": "test description",
"QueryName": "test",
"Source": "my-product",
"Target": "",
"Quantity": 10080,
"QuantityMin": 42,
"QuantityAvg": 42,
"QuantityMax": 42,
"Unit": "tps",
"PricePerUnit": 1,
"Discount": 0,
"Total": 10080,
"SubItems": {}
}
],
"Total": 10080
}
],
"Total": 10080
},
{
"Tenant": {
"Source": "umbrellacorp",
"Target": "96432"
},
"PeriodStart": "2022-01-01T00:00:00Z",
"PeriodEnd": "2022-01-31T00:00:00Z",
"Categories": [
{
"Source": "my-cluster:my-namespace",
"Target": "",
"Items": [
{
"Description": "test description",
"QueryName": "test",
"Source": "my-product",
"Target": "",
"Quantity": 9744,
"QuantityMin": 14,
"QuantityAvg": 14,
"QuantityMax": 14,
"Unit": "tps",
"PricePerUnit": 1,
"Discount": 0,
"Total": 9744,
"SubItems": {}
}
],
"Total": 9744
}
],
"Total": 9744
}
]
18 changes: 10 additions & 8 deletions pkg/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,11 @@ func processSample(ctx context.Context, tx *sqlx.Tx, ts time.Time, query db.Quer
}

var upsertedTenant db.Tenant
if upsertTenant(ctx, tx, &upsertedTenant, db.Tenant{Source: skey.Tenant}); err != nil {
return err
err = upsertTenant(ctx, tx, &upsertedTenant, db.Tenant{
Source: skey.Tenant,
}, ts)
if err != nil {
return fmt.Errorf("failed to upsert tenant '%s': %w", skey.Tenant, err)
}

var upsertedCategory db.Category
Expand Down Expand Up @@ -215,19 +218,18 @@ func upsertCategory(ctx context.Context, tx *sqlx.Tx, dst *db.Category, src db.C
return nil
}

func upsertTenant(ctx context.Context, tx *sqlx.Tx, dst *db.Tenant, src db.Tenant) error {
err := db.GetNamedContext(ctx, tx, dst,
func upsertTenant(ctx context.Context, tx *sqlx.Tx, dst *db.Tenant, src db.Tenant, ts time.Time) error {
err := sqlx.GetContext(ctx, tx, dst,
`WITH
existing AS (
SELECT * FROM tenants WHERE source = :source
SELECT * FROM tenants WHERE source = $1 AND during @> $2::timestamptz
),
inserted AS (
INSERT INTO tenants (source)
SELECT :source WHERE NOT EXISTS (SELECT 1 FROM existing)
SELECT $1 WHERE NOT EXISTS (SELECT 1 FROM existing)
RETURNING *
)
SELECT * FROM inserted UNION ALL SELECT * FROM existing`,
src)
SELECT * FROM inserted UNION ALL SELECT * FROM existing`, src.Source, ts)
if err != nil {
return fmt.Errorf("failed to upsert tenant %+v: %w", src, err)
}
Expand Down

0 comments on commit 562ae0e

Please sign in to comment.