Skip to content

Commit

Permalink
feat(#139): query all incidents (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
firminochangani authored Jan 6, 2024
1 parent a7e8687 commit 632ffc3
Show file tree
Hide file tree
Showing 11 changed files with 468 additions and 56 deletions.
47 changes: 47 additions & 0 deletions api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,37 @@ paths:
$ref: '#/components/schemas/GetMonitorResponseTimeStatsPayload'
default:
$ref: '#/components/responses/DefaultError'
/incidents:
get:
tags:
- Incidents
operationId: getAllIncidents
summary: Get all incidents
parameters:
- name: page
in: query
required: false
schema:
type: integer
minimum: 1
- name: limit
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 100
security:
- BearerAuth: [ ]
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/GetAllIncidentsPayload'
default:
$ref: '#/components/responses/DefaultError'
/incidents/{incidentID}:
get:
tags:
Expand Down Expand Up @@ -420,6 +451,22 @@ components:
properties:
data:
$ref: '#/components/schemas/FullIncident'
GetAllIncidentsPayload:
required: [ page, per_page, page_count, total_count, data ]
properties:
data:
type: array
items:
$ref: '#/components/schemas/Incident'
page:
type: integer
per_page:
type: integer
page_count:
type: integer
total_count:
type: integer
format: int64
GenericResponse:
type: object
required:
Expand Down
1 change: 1 addition & 0 deletions cmd/service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func main() {
MonitorByID: observability.NewQueryDecorator[query.MonitorByID, *monitor.Monitor](query.NewMonitorByIdHandler(monitorRepository), logger),
IncidentByID: observability.NewQueryDecorator[query.IncidentByID, *monitor.Incident](query.NewIncidentByIdHandler(incidentRepository), logger),
AllMonitors: observability.NewQueryDecorator[query.AllMonitors, query.PaginatedResult[*monitor.Monitor]](query.NewAllMonitorsHandler(monitorRepository), logger),
AllIncidents: observability.NewQueryDecorator[query.AllIncidents, query.PaginatedResult[*monitor.Incident]](query.NewAllIncidentsHandler(incidentRepository), logger),
MonitorResponseTimeStats: observability.NewQueryDecorator[query.MonitorResponseTimeStats, []query.ResponseTimeStat](query.NewMonitorResponseTimeStatsHandler(monitorRepository), logger),

// IAM
Expand Down
49 changes: 49 additions & 0 deletions internal/adapters/psql/incident.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import (
"context"
"database/sql"
"errors"
"fmt"

"github.com/volatiletech/sqlboiler/v4/boil"
"github.com/volatiletech/sqlboiler/v4/queries/qm"

"github.com/flowck/dobermann/backend/internal/adapters/models"
"github.com/flowck/dobermann/backend/internal/app/query"
"github.com/flowck/dobermann/backend/internal/domain"
"github.com/flowck/dobermann/backend/internal/domain/monitor"
)
Expand All @@ -20,6 +23,52 @@ type IncidentRepository struct {
db boil.ContextExecutor
}

func (i IncidentRepository) FindAll(
ctx context.Context,
accountID domain.ID,
params query.PaginationParams,
) (query.PaginatedResult[*monitor.Incident], error) {
monitorModels, err := models.Monitors(models.MonitorWhere.AccountID.EQ(accountID.String()), qm.Select("id")).All(ctx, i.db)
if err != nil {
return query.PaginatedResult[*monitor.Incident]{}, fmt.Errorf("unable to query monitors of account %s due to: %v", accountID, err)
}

monitorIDs := make([]string, len(monitorModels))
for idx, m := range monitorModels {
monitorIDs[idx] = m.ID
}

mods := []qm.QueryMod{
models.IncidentWhere.MonitorID.IN(monitorIDs),
qm.Offset(mapPaginationParamsToOffset(params.Page, params.Limit)),
qm.Limit(params.Limit),
qm.OrderBy("created_at DESC"),
}

modelList, err := models.Incidents(mods...).All(ctx, i.db)
if err != nil {
return query.PaginatedResult[*monitor.Incident]{}, fmt.Errorf("error while querying monitors: %v", err)
}

count, err := models.Incidents(models.IncidentWhere.MonitorID.IN(monitorIDs)).Count(ctx, i.db)
if err != nil {
return query.PaginatedResult[*monitor.Incident]{}, fmt.Errorf("error while counting monitors: %v", err)
}

incidents, err := mapModelsToIncidents(modelList)
if err != nil {
return query.PaginatedResult[*monitor.Incident]{}, err
}

return query.PaginatedResult[*monitor.Incident]{
TotalCount: count,
Data: incidents,
Page: params.Page,
PerPage: params.Limit,
PageCount: mapPaginationPerPageCount(count, params.Limit),
}, nil
}

func (i IncidentRepository) FindByID(ctx context.Context, id domain.ID) (*monitor.Incident, error) {
model, err := models.FindIncident(ctx, i.db, id.String())
if errors.Is(err, sql.ErrNoRows) {
Expand Down
15 changes: 15 additions & 0 deletions internal/adapters/psql/incident_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/flowck/dobermann/backend/internal/app/query"
"github.com/flowck/dobermann/backend/internal/domain"
"github.com/flowck/dobermann/backend/internal/domain/monitor"
"github.com/flowck/dobermann/backend/tests"
Expand Down Expand Up @@ -34,6 +35,20 @@ func TestIncidentRepository_Lifecycle(t *testing.T) {
_, err := incidentRepository.FindByID(ctx, domain.NewID())
assert.ErrorIs(t, err, monitor.ErrIncidentNotFound)
})

t.Run("all_incidents", func(t *testing.T) {
t.Parallel()

result, err := incidentRepository.FindAll(ctx, account00.ID(), query.PaginationParams{
Page: 1,
Limit: 100,
})
require.NoError(t, err)

for _, foundIncident := range result.Data {
require.Equal(t, monitor00.ID(), foundIncident.MonitorID())
}
})
}

func assertIncident(t *testing.T, expected, found *monitor.Incident) {
Expand Down
5 changes: 5 additions & 0 deletions internal/adapters/psql/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ func NewIncidentRepositoryMock() IncidentRepositoryMock {
}
}

func (i IncidentRepositoryMock) FindAll(ctx context.Context, accountID domain.ID) ([]*monitor.Incident, error) {
//TODO implement me
panic("implement me")
}

func (i IncidentRepositoryMock) FindByID(ctx context.Context, id domain.ID) (*monitor.Incident, error) {
return nil, nil
}
Expand Down
1 change: 1 addition & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type Queries struct {
IncidentByID QueryHandler[query.IncidentByID, *monitor.Incident]
MonitorResponseTimeStats QueryHandler[query.MonitorResponseTimeStats, []query.ResponseTimeStat]
AllMonitors QueryHandler[query.AllMonitors, query.PaginatedResult[*monitor.Monitor]]
AllIncidents QueryHandler[query.AllIncidents, query.PaginatedResult[*monitor.Incident]]

// IAM
UserByID QueryHandler[query.UserByID, *account.User]
Expand Down
31 changes: 31 additions & 0 deletions internal/app/query/all_incidents.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package query

import (
"context"

"github.com/flowck/dobermann/backend/internal/domain"
"github.com/flowck/dobermann/backend/internal/domain/monitor"
)

type AllIncidents struct {
AccountID domain.ID
Params PaginationParams
}

type incidentsFinder interface {
FindAll(ctx context.Context, accID domain.ID, params PaginationParams) (PaginatedResult[*monitor.Incident], error)
}

type AllIncidentsHandler struct {
incidentsFinder incidentsFinder
}

func NewAllIncidentsHandler(incidentsFinder incidentsFinder) AllIncidentsHandler {
return AllIncidentsHandler{
incidentsFinder: incidentsFinder,
}
}

func (h AllIncidentsHandler) Execute(ctx context.Context, q AllIncidents) (PaginatedResult[*monitor.Incident], error) {
return h.incidentsFinder.FindAll(ctx, q.AccountID, q.Params)
}
23 changes: 23 additions & 0 deletions internal/ports/http/incidents.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,26 @@ func (h handlers) GetIncidentByID(c echo.Context, incidentID string) error {
Data: mapIncidentToFullIncidentResponse(foundIncident),
})
}

func (h handlers) GetAllIncidents(c echo.Context, params GetAllIncidentsParams) error {
user, err := retrieveUserFromCtx(c)
if err != nil {
return NewUnableToRetrieveUserFromCtx(err)
}

result, err := h.application.Queries.AllIncidents.Execute(c.Request().Context(), query.AllIncidents{
AccountID: user.AccountID,
Params: query.NewPaginationParams(params.Page, params.Limit),
})
if err != nil {
return NewHandlerError(err, "unable-to-get-incidents")
}

return c.JSON(http.StatusOK, GetAllIncidentsPayload{
Data: mapIncidentsToResponse(result.Data),
Page: result.Page,
PageCount: result.PageCount,
PerPage: result.PerPage,
TotalCount: result.TotalCount,
})
}
102 changes: 74 additions & 28 deletions internal/ports/http/server.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 632ffc3

Please sign in to comment.