Skip to content

Commit

Permalink
Query event types: add http handler (#73)
Browse files Browse the repository at this point in the history
* update setup instructions of web and server

* add http handler to query event types

* remove envid

* Query event types: implement repo (#74)

* wip

* implement repo and add integration tests

* add component test (#75)
  • Loading branch information
firminochangani authored Sep 8, 2024
1 parent 70f59db commit a6d4e42
Show file tree
Hide file tree
Showing 16 changed files with 614 additions and 90 deletions.
11 changes: 6 additions & 5 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

## Setup

1. Clone the repository `git clone git@github.com:subscribeddotdev/subscribed-backend.git`
2. Build the container with all the CLI tools that this repo depends on: `task setup`
3. Run the project: `task run`
4. View logs: `task logs`
1. Clone the repository `git clone git@github.com:subscribeddotdev/subscribed.git`
2. Navigate to the server folder: `cd subscribed/server`
3. Build the container with all the CLI tools that this repo depends on: `task setup`
4. Run the project: `task run`
5. View logs: `task logs`

## Running tests:

Expand All @@ -24,4 +25,4 @@
- Running migrations downwards `task mig:down`
- Generating handlers from the Open API spec `task openapi`
- Generating ORM models `task orm`
- Generating models from the event specification `task events`
- Generating models from the event specification `task events`
60 changes: 55 additions & 5 deletions server/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,7 @@ paths:
operationId: getApplications
summary: Returns a list of applications based on the org_id and environment_id
parameters:
- name: environment_id
in: query
required: true
schema:
type: string
- $ref: '#/components/parameters/environmentId'
- $ref: '#/components/parameters/paginationParamLimit'
- $ref: '#/components/parameters/paginationParamPage'
security:
Expand Down Expand Up @@ -224,6 +220,26 @@ paths:
description: CREATED
default:
$ref: '#/components/responses/DefaultError'
get:
tags:
- EventTypes
operationId: getEventTypes
summary: Get event types by org_id
security:
- BearerAuth: [ ]
- ApiKeyAuth: [ ]
parameters:
- $ref: '#/components/parameters/paginationParamLimit'
- $ref: '#/components/parameters/paginationParamPage'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/GetEventTypesPayload'
default:
$ref: '#/components/responses/DefaultError'
/signup:
post:
tags:
Expand Down Expand Up @@ -288,6 +304,12 @@ components:
required: true
schema:
type: string
environmentId:
name: environmentID
in: query
required: true
schema:
type: string
paginationParamLimit:
name: limit
description: The number of items per page
Expand Down Expand Up @@ -409,6 +431,34 @@ components:
type: string
schema_example:
type: string
EventType:
required: [id, name, description, schema, schema_example, created_at]
properties:
id:
type: string
name:
type: string
description:
type: string
created_at:
type: string
format: date-time
schema:
type: string
schema_example:
type: string
archived_at:
type: string
format: date-time
GetEventTypesPayload:
required: [ pagination, data ]
properties:
pagination:
$ref: '#/components/schemas/Pagination'
data:
type: array
items:
$ref: '#/components/schemas/EventType'
# API Keys
CreateApiKeyRequest:
required: [name, environment_id]
Expand Down
3 changes: 3 additions & 0 deletions server/cmd/service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ func run(logger *logs.Logger) error {
// Applications
AllApplications: observability.NewQueryDecorator[query.AllApplications, query.Paginated[[]domain.Application]](query.NewAllApplicationsHandler(applicationRepo), logger),
Application: observability.NewQueryDecorator[query.Application, *domain.Application](query.NewApplicationHandler(applicationRepo), logger),

// Event types
AllEventTypes: observability.NewQueryDecorator[query.AllEventTypes, query.Paginated[[]domain.EventType]](query.NewAllEventTypesHandler(eventTypeRepo), logger),
},
}

Expand Down
9 changes: 7 additions & 2 deletions server/internal/adapters/psql/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestApplicationRepository_FindAll(t *testing.T) {
result, err := applicationRepo.FindAll(
ctx,
domain.EnvironmentID(env.ID),
iam.OrgID(env.ID),
"", //TODO: add me
query.NewPaginationParams(tests.ToPtr(30), tests.ToPtr(5)),
)
require.NoError(t, err)
Expand All @@ -49,7 +49,12 @@ func TestApplicationRepository_FindAll(t *testing.T) {

for i := 0; i < totalPages; i++ {
currentPage := i + 1
result, err := applicationRepo.FindAll(ctx, domain.EnvironmentID(env.ID), iam.OrgID(env.ID), query.NewPaginationParams(&currentPage, &perPage))
result, err := applicationRepo.FindAll(
ctx,
domain.EnvironmentID(env.ID),
"", //TODO: add me
query.NewPaginationParams(&currentPage, &perPage),
)
require.NoError(t, err)
require.NotEmpty(t, result.Data)
require.Equal(t, perPage, result.PerPage)
Expand Down
52 changes: 52 additions & 0 deletions server/internal/adapters/psql/event_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"fmt"

"github.com/subscribeddotdev/subscribed/server/internal/adapters/models"
"github.com/subscribeddotdev/subscribed/server/internal/app/query"
"github.com/subscribeddotdev/subscribed/server/internal/domain"
"github.com/subscribeddotdev/subscribed/server/internal/domain/iam"
"github.com/volatiletech/null/v8"
"github.com/volatiletech/sqlboiler/v4/boil"
"github.com/volatiletech/sqlboiler/v4/queries/qm"
)

type EventTypeRepository struct {
Expand Down Expand Up @@ -39,3 +42,52 @@ func (e EventTypeRepository) Insert(ctx context.Context, eventType *domain.Event

return nil
}

func (e EventTypeRepository) FindAll(
ctx context.Context,
orgID iam.OrgID,
pagination query.PaginationParams,
) (query.Paginated[[]domain.EventType], error) {
total, err := models.EventTypes(models.EventTypeWhere.OrgID.EQ(orgID.String())).Count(ctx, e.db)
if err != nil {
return query.Paginated[[]domain.EventType]{}, fmt.Errorf("error counting event types: %v", err)
}

rows, err := models.EventTypes(
models.EventTypeWhere.OrgID.EQ(orgID.String()),
qm.Offset(mapPaginationParamsToSqlOffset(pagination)),
qm.Limit(pagination.Limit()),
qm.OrderBy("created_at DESC"),
).All(ctx, e.db)
if err != nil {
return query.Paginated[[]domain.EventType]{}, fmt.Errorf("error querying event types: %v", err)
}

return query.Paginated[[]domain.EventType]{
Total: int(total),
PerPage: len(rows),
CurrentPage: pagination.Page(),
TotalPages: getPaginationTotalPages(total, pagination.Limit()),
Data: mapRowsToEventTypes(rows),
}, nil
}

func mapRowsToEventTypes(rows []*models.EventType) []domain.EventType {
eventTypes := make([]domain.EventType, len(rows))
for i, row := range rows {
eventType := domain.UnMarshallEventType(
domain.EventTypeID(row.ID),
row.OrgID,
row.Name,
row.Description.String,
row.Schema.String,
row.SchemaExample.String,
row.CreatedAt,
row.ArchivedAt.Ptr(),
)

eventTypes[i] = *eventType
}

return eventTypes
}
56 changes: 56 additions & 0 deletions server/internal/adapters/psql/event_type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package psql_test

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/subscribeddotdev/subscribed/server/internal/app/query"
"github.com/subscribeddotdev/subscribed/server/internal/domain"
"github.com/subscribeddotdev/subscribed/server/internal/domain/iam"
"github.com/subscribeddotdev/subscribed/server/tests"
"github.com/subscribeddotdev/subscribed/server/tests/fixture"
)

func TestEventTypesRepository_FindAll(t *testing.T) {
ff := fixture.NewFactory(t, ctx, db)
org := ff.NewOrganization().Save()

eventTypesFixtureCount := 20
for i := 0; i < eventTypesFixtureCount; i++ {
ff.NewEventType().WithOrgID(org.ID).Save()
}

t.Run("return_an_empty_slice_when_page_is_out_of_bounds", func(t *testing.T) {
result, err := eventTypeRepo.FindAll(
ctx,
iam.OrgID(org.ID),
query.NewPaginationParams(tests.ToPtr(30), tests.ToPtr(5)),
)
require.NoError(t, err)
require.Empty(t, result.Data)
require.Equal(t, 0, result.PerPage)
})

t.Run("iteratively_query_event_types_from_different_pages", func(t *testing.T) {
perPage := 5
totalPages := eventTypesFixtureCount / 5
queriedAppIDs := make(map[string]domain.EventType)

for i := 0; i < totalPages; i++ {
currentPage := i + 1
result, err := eventTypeRepo.FindAll(ctx, iam.OrgID(org.ID), query.NewPaginationParams(&currentPage, &perPage))
require.NoError(t, err)
require.NotEmpty(t, result.Data)
require.Equal(t, perPage, result.PerPage)
require.Equal(t, currentPage, result.CurrentPage)
require.Equal(t, eventTypesFixtureCount, result.Total)
require.Equal(t, totalPages, result.TotalPages)

for _, app := range result.Data {
_, exists := queriedAppIDs[app.ID().String()]
require.Falsef(t, exists, "event type must not have already been returned: app id '%s'", app.ID())
queriedAppIDs[app.ID().String()] = app
}
}
})
}
2 changes: 2 additions & 0 deletions server/internal/adapters/psql/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var (
ctx context.Context
environmentRepo *psql.EnvironmentRepository
applicationRepo *psql.ApplicationRepository
eventTypeRepo *psql.EventTypeRepository
endpointRepo *psql.EndpointRepository
organizationRepo *psql.OrganizationRepository
apiKeyRepo *psql.ApiKeyRepository
Expand Down Expand Up @@ -51,6 +52,7 @@ func TestMain(m *testing.M) {
organizationRepo = psql.NewOrganizationRepository(db)
apiKeyRepo = psql.NewApiKeyRepository(db)
msgRepo = psql.NewMessageRepository(db)
eventTypeRepo = psql.NewEventTypeRepository(db)

os.Exit(m.Run())
}
3 changes: 3 additions & 0 deletions server/internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ type Query struct {
// Applications
Application QueryHandler[query.Application, *domain.Application]
AllApplications QueryHandler[query.AllApplications, query.Paginated[[]domain.Application]]

// Event types
AllEventTypes QueryHandler[query.AllEventTypes, query.Paginated[[]domain.EventType]]
}

type App struct {
Expand Down
31 changes: 31 additions & 0 deletions server/internal/app/query/all_event_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package query

import (
"context"

"github.com/subscribeddotdev/subscribed/server/internal/domain"
"github.com/subscribeddotdev/subscribed/server/internal/domain/iam"
)

type AllEventTypes struct {
PaginationParams
OrgID string
}

type eventTypesFinder interface {
FindAll(ctx context.Context, orgID iam.OrgID, pagination PaginationParams) (Paginated[[]domain.EventType], error)
}

type allEventTypesHandler struct {
eventTypesFinder eventTypesFinder
}

func NewAllEventTypesHandler(eventTypesFinder eventTypesFinder) allEventTypesHandler {
return allEventTypesHandler{
eventTypesFinder: eventTypesFinder,
}
}

func (h allEventTypesHandler) Execute(ctx context.Context, q AllEventTypes) (Paginated[[]domain.EventType], error) {
return h.eventTypesFinder.FindAll(ctx, iam.OrgID(q.OrgID), q.PaginationParams)
}
38 changes: 36 additions & 2 deletions server/internal/ports/http/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func (h handlers) GetApplications(c echo.Context, params GetApplicationsParams)

result, err := h.application.Query.AllApplications.Execute(c.Request().Context(), query.AllApplications{
PaginationParams: query.NewPaginationParams(params.Page, params.Limit),
EnvironmentID: params.EnvironmentId,
EnvironmentID: params.EnvironmentID,
OrgID: claims.OrganizationID,
})
if err != nil {
Expand Down Expand Up @@ -301,7 +301,7 @@ func (h handlers) GetApplicationById(c echo.Context, applicationID ApplicationId
OrgID: claims.OrganizationID,
})
if errors.Is(err, domain.ErrAppNotFound) {
return NewHandlerErrorWithStatus(err, "error-retrieving-application", http.StatusNotFound)
return NewHandlerErrorWithStatus(err, "error-application-not-found", http.StatusNotFound)
}

if err != nil {
Expand All @@ -316,6 +316,40 @@ func (h handlers) GetApplicationById(c echo.Context, applicationID ApplicationId
}})
}

func (h handlers) GetEventTypes(c echo.Context, params GetEventTypesParams) error {
claims, err := h.resolveJwtClaimsFromCtx(c)
if err != nil {
return err
}

result, err := h.application.Query.AllEventTypes.Execute(c.Request().Context(), query.AllEventTypes{
PaginationParams: query.NewPaginationParams(params.Page, params.Limit),
OrgID: claims.OrganizationID,
})
if err != nil {
return NewHandlerError(err, "error-retrieving-event-types")
}

data := make([]EventType, len(result.Data))

for i, et := range result.Data {
data[i] = EventType{
Id: et.ID().String(),
Name: et.Name(),
Description: et.Description(),
Schema: et.Schema(),
SchemaExample: et.SchemaExample(),
CreatedAt: et.CreatedAt(),
ArchivedAt: et.ArchivedAt(),
}
}

return c.JSON(http.StatusOK, GetEventTypesPayload{
Data: data,
Pagination: mapToPaginationResponse(result),
})
}

func (h handlers) resolveJwtClaimsFromCtx(c echo.Context) (*jwtCustomClaims, error) {
claims, ok := c.Get("user_claims").(*jwtCustomClaims)
if !ok {
Expand Down
Loading

0 comments on commit a6d4e42

Please sign in to comment.