diff --git a/api/endpoint.go b/api/endpoint.go index 1fe0690..5a5c625 100644 --- a/api/endpoint.go +++ b/api/endpoint.go @@ -83,6 +83,9 @@ func retrieveSummaryEndpoint(svc callhome.Service) endpoint.Endpoint { } return telemetrySummaryRes{ Countries: summary.Countries, + Cities: summary.Cities, + Services: summary.Services, + Versions: summary.Versions, TotalDeployments: summary.TotalDeployments, }, nil } diff --git a/api/responses.go b/api/responses.go index c03516d..1fbc7c7 100644 --- a/api/responses.go +++ b/api/responses.go @@ -89,6 +89,9 @@ func (res uiRes) Headers() map[string]string { type telemetrySummaryRes struct { Countries []callhome.CountrySummary `json:"countries,omitempty"` + Cities []string `json:"cities,omitempty"` + Services []string `json:"services,omitempty"` + Versions []string `json:"versions,omitempty"` TotalDeployments int `json:"total_deployments,omitempty"` } diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 80ed952..e3a6a41 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -238,6 +238,18 @@ components: type: string number_of_deployments: type: integer + cities: + type: array + items: + type: string + services: + type: array + items: + type: string + versions: + type: array + items: + type: string securitySchemes: ApiKeyAuth: type: apiKey diff --git a/service.go b/service.go index 4d0fbe6..ddc8d03 100644 --- a/service.go +++ b/service.go @@ -57,14 +57,18 @@ func (ts *telemetryService) Save(ctx context.Context, t Telemetry) error { } func (ts *telemetryService) RetrieveSummary(ctx context.Context, filters TelemetryFilters) (TelemetrySummary, error) { - return ts.repo.RetrieveDistinctIPsCountries(ctx, filters) + return ts.repo.RetrieveSummary(ctx, filters) } // ServeUI gets the callhome index html page func (ts *telemetryService) ServeUI(ctx context.Context, filters TelemetryFilters) ([]byte, error) { tmpl := template.Must(template.ParseFiles("./web/template/index.html")) - summary, err := ts.repo.RetrieveDistinctIPsCountries(ctx, filters) + summary, err := ts.repo.RetrieveSummary(ctx, filters) + if err != nil { + return nil, err + } + unfilteredSummary, err := ts.repo.RetrieveSummary(ctx, TelemetryFilters{}) if err != nil { return nil, err } @@ -81,6 +85,7 @@ func (ts *telemetryService) ServeUI(ctx context.Context, filters TelemetryFilter if err != nil { return nil, err } + var from, to string if !filters.From.IsZero() { from = filters.From.Format(time.DateOnly) @@ -89,19 +94,28 @@ func (ts *telemetryService) ServeUI(ctx context.Context, filters TelemetryFilter to = filters.To.Format(time.DateOnly) } data := struct { - Countries string - NoDeployments int - NoCountries int - MapData string - From string - To string + Countries string + Cities string + FilterCountries []CountrySummary + FilterCities []string + FilterServices []string + FilterVersions []string + NoDeployments int + NoCountries int + MapData string + From string + To string }{ - Countries: string(countries), - NoDeployments: summary.TotalDeployments, - NoCountries: len(summary.Countries), - MapData: string(pg), - From: from, - To: to, + Countries: string(countries), + FilterCountries: unfilteredSummary.Countries, + FilterCities: unfilteredSummary.Cities, + FilterServices: unfilteredSummary.Services, + FilterVersions: unfilteredSummary.Versions, + NoDeployments: summary.TotalDeployments, + NoCountries: len(summary.Countries), + MapData: string(pg), + From: from, + To: to, } var res bytes.Buffer diff --git a/telemetry.go b/telemetry.go index 7d4a234..5a83c86 100644 --- a/telemetry.go +++ b/telemetry.go @@ -47,6 +47,9 @@ type CountrySummary struct { type TelemetrySummary struct { Countries []CountrySummary `json:"countries,omitempty"` + Cities []string `json:"cities,omitempty"` + Services []string `json:"services,omitempty"` + Versions []string `json:"versions,omitempty"` TotalDeployments int `json:"total_deployments,omitempty"` } @@ -58,6 +61,6 @@ type TelemetryRepo interface { // RetrieveAll retrieves all telemetry events. RetrieveAll(ctx context.Context, pm PageMetadata, filters TelemetryFilters) (TelemetryPage, error) - // RetrieveDistinctIPsCOuntries gets distinct ip addresses and countries from database. - RetrieveDistinctIPsCountries(ctx context.Context, filters TelemetryFilters) (TelemetrySummary, error) + // RetrieveSummary gets distinct countries, cities,services and versions in a summarised form. + RetrieveSummary(ctx context.Context, filters TelemetryFilters) (TelemetrySummary, error) } diff --git a/timescale/mocks/mocks.go b/timescale/mocks/mocks.go index 74796f5..b59c56a 100644 --- a/timescale/mocks/mocks.go +++ b/timescale/mocks/mocks.go @@ -30,8 +30,7 @@ func (mr *mockRepo) Save(ctx context.Context, t callhome.Telemetry) error { return r0 } -// RetrieveDistinctIPsCountries retrieve distinct -func (*mockRepo) RetrieveDistinctIPsCountries(ctx context.Context, filter callhome.TelemetryFilters) (callhome.TelemetrySummary, error) { +func (*mockRepo) RetrieveSummary(ctx context.Context, filter callhome.TelemetryFilters) (callhome.TelemetrySummary, error) { return callhome.TelemetrySummary{}, nil } diff --git a/timescale/timescale.go b/timescale/timescale.go index b992c2a..952e23e 100644 --- a/timescale/timescale.go +++ b/timescale/timescale.go @@ -126,16 +126,16 @@ func (r repo) Save(ctx context.Context, t callhome.Telemetry) error { } -// RetrieveDistinctIPsCountries retrieve distinct -func (r repo) RetrieveDistinctIPsCountries(ctx context.Context, filters callhome.TelemetryFilters) (callhome.TelemetrySummary, error) { +// RetrieveSummary retrieve distinct +func (r repo) RetrieveSummary(ctx context.Context, filters callhome.TelemetryFilters) (callhome.TelemetrySummary, error) { filterQuery, params := generateQuery(filters) + var summary callhome.TelemetrySummary q := fmt.Sprintf(`select count(distinct ip_address), country from telemetry %s group by country;`, filterQuery) rows, err := r.db.NamedQuery(q, params) if err != nil { return callhome.TelemetrySummary{}, err } defer rows.Close() - var summary callhome.TelemetrySummary for rows.Next() { var val callhome.CountrySummary if err := rows.StructScan(&val); err != nil { @@ -146,6 +146,48 @@ func (r repo) RetrieveDistinctIPsCountries(ctx context.Context, filters callhome for _, country := range summary.Countries { summary.TotalDeployments += country.NoDeployments } + + q1 := fmt.Sprintf(`select distinct city from telemetry %s;`, filterQuery) + cityRows, err := r.db.NamedQuery(q1, params) + if err != nil { + return callhome.TelemetrySummary{}, err + } + defer cityRows.Close() + for cityRows.Next() { + var val string + if err := cityRows.Scan(&val); err != nil { + return callhome.TelemetrySummary{}, err + } + summary.Cities = append(summary.Cities, val) + } + + q2 := fmt.Sprintf(`select distinct service from telemetry %s;`, filterQuery) + serviceRows, err := r.db.NamedQuery(q2, params) + if err != nil { + return callhome.TelemetrySummary{}, err + } + defer serviceRows.Close() + for serviceRows.Next() { + var val string + if err := serviceRows.Scan(&val); err != nil { + return callhome.TelemetrySummary{}, err + } + summary.Services = append(summary.Services, val) + } + + q3 := fmt.Sprintf(`select distinct mf_version from telemetry %s;`, filterQuery) + versionRows, err := r.db.NamedQuery(q3, params) + if err != nil { + return callhome.TelemetrySummary{}, err + } + defer versionRows.Close() + for versionRows.Next() { + var val string + if err := versionRows.Scan(&val); err != nil { + return callhome.TelemetrySummary{}, err + } + summary.Versions = append(summary.Versions, val) + } return summary, nil } diff --git a/timescale/tracing/tracing.go b/timescale/tracing/tracing.go index ab1a213..5c8a488 100644 --- a/timescale/tracing/tracing.go +++ b/timescale/tracing/tracing.go @@ -8,9 +8,9 @@ import ( ) const ( - retrieveAllOp = "retrieve_all_op" - retrieveDistinctIPsCountriesOp = "retrieve_distinct_IP_countries_op" - saveOp = "save_op" + retrieveAllOp = "retrieve_all_op" + retrieveSummaryOp = "retrieve_summary_op" + saveOp = "save_op" ) var _ callhome.TelemetryRepo = (*repoTracer)(nil) @@ -35,11 +35,11 @@ func (rt *repoTracer) RetrieveAll(ctx context.Context, pm callhome.PageMetadata, return rt.repo.RetrieveAll(ctx, pm, filter) } -// RetrieveDistinctIPsCountries adds tracing middleware to retrieve distinct ips countries method. -func (rt *repoTracer) RetrieveDistinctIPsCountries(ctx context.Context, filter callhome.TelemetryFilters) (callhome.TelemetrySummary, error) { - ctx, span := rt.tracer.Start(ctx, retrieveDistinctIPsCountriesOp) +// RetrieveSummary adds tracing middleware to retrieve summary method. +func (rt *repoTracer) RetrieveSummary(ctx context.Context, filter callhome.TelemetryFilters) (callhome.TelemetrySummary, error) { + ctx, span := rt.tracer.Start(ctx, retrieveSummaryOp) defer span.End() - return rt.repo.RetrieveDistinctIPsCountries(ctx, filter) + return rt.repo.RetrieveSummary(ctx, filter) } // Save adds tracing middleware to save method. diff --git a/web/static/style.css b/web/static/style.css index 837ed89..02f0012 100644 --- a/web/static/style.css +++ b/web/static/style.css @@ -18,31 +18,41 @@ body { } .main-content { flex-grow: 1; - height: 95%; - width: 100%; - -} -#map { height: 100%; width: 100%; } -#filter-container { +.btn { + margin-left: 20px; + margin-right: 20px; + padding: 10px 20px; + text-align: center; + line-height: 10px; + margin-top: 10px; +} +.list-group-item{ display: flex; - justify-content: center; - margin-bottom: 10px; - margin-top: 10px; - height: 5%; + justify-content: space-between; +} +.badge { + max-height: 20px; +} +#map { + height: 100%; width: 100%; } - #filter-form { - display: flex; + display: fixed; gap: 20px; } - +#myButton { + position: absolute; + top: 20px; + right: 20px; + z-index: 1000; +} #from-date, #to-date { - width: 200px; + width: 150px; height: 30px; border: 1px solid #ccc; border-radius: 4px; diff --git a/web/template/index.html b/web/template/index.html index b6d0873..cddee46 100644 --- a/web/template/index.html +++ b/web/template/index.html @@ -4,8 +4,9 @@ + - + @@ -23,21 +24,65 @@