Skip to content

Commit

Permalink
feat: support search filter for contacts
Browse files Browse the repository at this point in the history
Dynamic querying is not the prettiest with SQLC but I think it went
alright, forgive me lord if I have sinned.
  • Loading branch information
crazybolillo committed Oct 17, 2024
1 parent 933ae17 commit 48413f7
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 9 deletions.
14 changes: 14 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,20 @@ paths:
in: query
name: pageSize
type: integer
- description: Filter by name. Asterisks can be used to represent any character(s)
in: query
name: name
type: string
- description: Filter by phone number. Asterisks can be used to represent any
character(s)
in: query
name: phone
type: string
- default: and
description: Whether to join filter values with AND/OR logic.
in: query
name: op
type: string
produces:
- application/json
responses:
Expand Down
16 changes: 15 additions & 1 deletion internal/handler/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"encoding/json"
"github.com/crazybolillo/eryth/internal/query"
"github.com/crazybolillo/eryth/internal/service"
"github.com/crazybolillo/eryth/pkg/model"
"github.com/go-chi/chi/v5"
"log/slog"
"net/http"
"strings"
)

type Contact struct {
Expand All @@ -23,6 +25,9 @@ func (p *Contact) Router() chi.Router {
// @Summary List all contacts in the system.
// @Param page query int false "Zero based page to fetch" default(0)
// @Param pageSize query int false "Max amount of results to be returned" default(20)
// @Param name query string false "Filter by name. Asterisks can be used to represent any character(s)"
// @Param phone query string false "Filter by phone number. Asterisks can be used to represent any character(s)"
// @Param op query string false "Whether to join filter values with AND/OR logic." default(and)
// @Produce json
// @Success 200 {object} model.ContactPage
// @Tags contacts
Expand All @@ -40,7 +45,16 @@ func (p *Contact) list(w http.ResponseWriter, r *http.Request) {
return
}

res, err := p.Service.Paginate(r.Context(), page, pageSize)
res, err := p.Service.Paginate(
r.Context(),
model.ContactPageFilter{
Name: strings.ReplaceAll(r.URL.Query().Get("name"), "*", "%"),
Phone: strings.ReplaceAll(r.URL.Query().Get("phone"), "*", "%"),
Operator: strings.ToLower(r.URL.Query().Get("op")),
},
page,
pageSize,
)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
slog.Error("Failed to list contacts", slog.String("path", r.URL.Path), slog.String("reason", err.Error()))
Expand Down
12 changes: 10 additions & 2 deletions internal/service/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package service
import (
"context"
"errors"
"github.com/crazybolillo/eryth/internal/db"
"github.com/crazybolillo/eryth/internal/sqlc"
"github.com/crazybolillo/eryth/pkg/model"
"github.com/jackc/pgx/v5"
Expand All @@ -12,18 +13,25 @@ type Contact struct {
Cursor
}

func (c *Contact) Paginate(ctx context.Context, page, size int) (model.ContactPage, error) {
func (c *Contact) Paginate(ctx context.Context, filter model.ContactPageFilter, page, size int) (model.ContactPage, error) {
queries := sqlc.New(c.Cursor)

rows, err := queries.ListContacts(ctx, sqlc.ListContactsParams{
Name: db.Text(filter.Name),
Phone: db.Text(filter.Phone),
Limit: int32(size),
Offset: int32(page),
Op: db.Text(filter.Operator),
})
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
return model.ContactPage{}, err
}

count, err := queries.CountEndpoints(ctx)
count, err := queries.CountContacts(ctx, sqlc.CountContactsParams{
Name: db.Text(filter.Name),
Phone: db.Text(filter.Phone),
Op: db.Text(filter.Operator),
})
if err != nil {
return model.ContactPage{}, err
}
Expand Down
33 changes: 28 additions & 5 deletions internal/sqlc/queries.sql.go

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

6 changes: 6 additions & 0 deletions pkg/model/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ type ContactPage struct {
Retrieved int `json:"retrieved"`
Contacts []Contact `json:"contacts"`
}

type ContactPageFilter struct {
Name string
Phone string
Operator string
}
10 changes: 9 additions & 1 deletion queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ FROM
ps_endpoints pe
INNER JOIN
ery_extension ee ON ee.endpoint_id = pe.sid
WHERE CASE
WHEN @op = 'or' THEN (pe.callerid ILIKE '"' || @name || '" <%>') OR (ee.extension LIKE @phone)
ELSE (pe.callerid ILIKE '"' || @name || '" <%>' OR @name IS NULL) AND (ee.extension LIKE @phone OR @phone IS NULL)
END
LIMIT
$1
OFFSET
Expand All @@ -149,4 +153,8 @@ SELECT
FROM
ps_endpoints pe
INNER JOIN
ery_extension ee ON ee.endpoint_id = pe.sid;
ery_extension ee ON ee.endpoint_id = pe.sid
WHERE CASE
WHEN @op = 'or' THEN (pe.callerid ILIKE '"' || @name || '" <%>') OR (ee.extension LIKE @phone)
ELSE (pe.callerid ILIKE '"' || @name || '" <%>' OR @name IS NULL) AND (ee.extension LIKE @phone OR @phone IS NULL)
END;

0 comments on commit 48413f7

Please sign in to comment.