Skip to content

Commit

Permalink
Add pagination to retro and storyboard pages and improve query perfor…
Browse files Browse the repository at this point in the history
…mance (#544)
  • Loading branch information
StevenWeathers committed Mar 29, 2024
1 parent fadf482 commit 057291b
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 45 deletions.
48 changes: 37 additions & 11 deletions internal/db/retro/retro.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,20 +232,46 @@ func (d *Service) RetroGet(RetroID string, UserID string) (*thunderdome.Retro, e
}

// RetroGetByUser gets a list of retros by UserID
func (d *Service) RetroGetByUser(UserID string) ([]*thunderdome.Retro, error) {
func (d *Service) RetroGetByUser(UserID string, Limit int, Offset int) ([]*thunderdome.Retro, int, error) {
var retros = make([]*thunderdome.Retro, 0)
var Count int

e := d.DB.QueryRow(`
SELECT COUNT(*) FROM thunderdome.retro r
LEFT JOIN thunderdome.retro_user u ON r.id = u.retro_id
WHERE u.user_id = $1 AND u.abandoned = false;
`, UserID).Scan(
&Count,
)
if e != nil {
return nil, Count, fmt.Errorf("get retros by user count query error: %v", e)
}

retroRows, retrosErr := d.DB.Query(`
SELECT b.id, b.name, b.owner_id, b.format, b.phase, b.created_date, b.updated_date,
WITH user_teams AS (
SELECT t.id, t.name FROM thunderdome.team_user tu
LEFT JOIN thunderdome.team t ON t.id = tu.team_id
WHERE tu.user_id = $1
),
team_retros AS (
SELECT id FROM thunderdome.retro WHERE team_id IN (SELECT id FROM user_teams)
),
user_retros AS (
SELECT u.retro_id AS id FROM thunderdome.retro_user u
WHERE u.user_id = $1 AND u.abandoned = false
),
retros AS (
SELECT id from user_retros UNION ALL SELECT id FROM team_retros
)
SELECT r.id, r.name, r.owner_id, r.format, r.phase, r.created_date, r.updated_date,
MIN(COALESCE(t.name, '')) as teamName
FROM thunderdome.retro b
LEFT JOIN thunderdome.retro_user su ON b.id = su.retro_id
LEFT JOIN thunderdome.team t ON t.id = b.team_id
LEFT JOIN thunderdome.team_user tu ON tu.team_id = t.id
WHERE (su.user_id = $1 AND su.abandoned = false) OR tu.user_id = $1
GROUP BY b.id ORDER BY b.created_date DESC;
`, UserID)
FROM thunderdome.retro r
LEFT JOIN user_teams t ON t.id = r.team_id
WHERE r.id IN (SELECT id FROM retros)
GROUP BY r.id ORDER BY r.created_date DESC LIMIT $2 OFFSET $3;
`, UserID, Limit, Offset)
if retrosErr != nil {
return nil, fmt.Errorf("get retro by user query error: %v", retrosErr)
return nil, Count, fmt.Errorf("get retro by user query error: %v", retrosErr)
}

defer retroRows.Close()
Expand All @@ -269,7 +295,7 @@ func (d *Service) RetroGetByUser(UserID string) ([]*thunderdome.Retro, error) {
}
}

return retros, nil
return retros, Count, nil
}

// RetroConfirmFacilitator confirms the user is a facilitator of the retro
Expand Down
48 changes: 37 additions & 11 deletions internal/db/storyboard/storyboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,20 +216,46 @@ func (d *Service) GetStoryboard(StoryboardID string, UserID string) (*thunderdom
}

// GetStoryboardsByUser gets a list of storyboards by UserID
func (d *Service) GetStoryboardsByUser(UserID string) ([]*thunderdome.Storyboard, int, error) {
func (d *Service) GetStoryboardsByUser(UserID string, Limit int, Offset int) ([]*thunderdome.Storyboard, int, error) {
var Count int
var storyboards = make([]*thunderdome.Storyboard, 0)

e := d.DB.QueryRow(`
SELECT COUNT(*) FROM thunderdome.storyboard s
LEFT JOIN thunderdome.storyboard_user u ON s.id = u.storyboard_id
WHERE u.user_id = $1 AND u.abandoned = false;
`, UserID).Scan(
&Count,
)
if e != nil {
return nil, Count, fmt.Errorf("get storyboards by user count query error: %v", e)
}

storyboardRows, storyboardsErr := d.DB.Query(`
SELECT b.id, b.name, b.owner_id, b.created_date, b.updated_date,
WITH user_teams AS (
SELECT t.id, t.name FROM thunderdome.team_user tu
LEFT JOIN thunderdome.team t ON t.id = tu.team_id
WHERE tu.user_id = $1
),
team_storyboards AS (
SELECT id FROM thunderdome.storyboard WHERE team_id IN (SELECT id FROM user_teams)
),
user_storyboards AS (
SELECT u.storyboard_id AS id FROM thunderdome.storyboard_user u
WHERE u.user_id = $1 AND u.abandoned = false
),
storyboards AS (
SELECT id from user_storyboards UNION ALL SELECT id FROM team_storyboards
)
SELECT s.id, s.name, s.owner_id, s.created_date, s.updated_date,
min(COALESCE(t.name, '')) as team_name
FROM thunderdome.storyboard b
LEFT JOIN thunderdome.storyboard_user su ON b.id = su.storyboard_id
LEFT JOIN thunderdome.team t ON t.id = b.team_id
LEFT JOIN thunderdome.team_user tu ON tu.team_id = t.id
WHERE (su.user_id = $1 AND su.abandoned = false) OR (tu.user_id = $1)
GROUP BY b.id ORDER BY b.created_date DESC;
`, UserID)
FROM thunderdome.storyboard s
LEFT JOIN user_teams t ON t.id = s.team_id
WHERE s.id IN (SELECT id FROM storyboards)
GROUP BY s.id ORDER BY s.created_date DESC LIMIT $2 OFFSET $3;
`, UserID, Limit, Offset)
if storyboardsErr != nil {
return nil, 0, fmt.Errorf("get storyboards by user query error: %v", storyboardsErr)
return nil, Count, fmt.Errorf("get storyboards by user query error: %v", storyboardsErr)
}

defer storyboardRows.Close()
Expand All @@ -254,7 +280,7 @@ func (d *Service) GetStoryboardsByUser(UserID string) ([]*thunderdome.Storyboard
}
}

return storyboards, 0, nil
return storyboards, Count, nil
}

// ConfirmStoryboardFacilitator confirms the user is a facilitator of the storyboard
Expand Down
11 changes: 9 additions & 2 deletions internal/http/retro.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,23 @@ func (s *Service) handleRetroGet() http.HandlerFunc {
// @Router /users/{userId}/retros [get]
func (s *Service) handleRetrosGetByUser() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
Limit, Offset := getLimitOffsetFromRequest(r)
vars := mux.Vars(r)
UserID := vars["userId"]

retros, err := s.RetroDataSvc.RetroGetByUser(UserID)
retros, Count, err := s.RetroDataSvc.RetroGetByUser(UserID, Limit, Offset)
if err != nil {
http.NotFound(w, r)
return
}

s.Success(w, r, http.StatusOK, retros, nil)
Meta := &pagination{
Count: Count,
Offset: Offset,
Limit: Limit,
}

s.Success(w, r, http.StatusOK, retros, Meta)
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/http/storyboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (s *Service) handleGetUserStoryboards() http.HandlerFunc {
vars := mux.Vars(r)
UserID := vars["userId"]

storyboards, Count, err := s.StoryboardDataSvc.GetStoryboardsByUser(UserID)
storyboards, Count, err := s.StoryboardDataSvc.GetStoryboardsByUser(UserID, Limit, Offset)
if err != nil {
s.Logger.Ctx(ctx).Error("handleGetUserStoryboards error", zap.Error(err), zap.Int("limit", Limit),
zap.Int("offset", Offset), zap.String("entity_user_id", UserID),
Expand Down
2 changes: 1 addition & 1 deletion thunderdome/retro.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ type RetroDataSvc interface {
TeamRetroCreate(ctx context.Context, TeamID string, OwnerID string, RetroName string, Format string, JoinCode string, FacilitatorCode string, MaxVotes int, BrainstormVisibility string) (*Retro, error)
EditRetro(RetroID string, RetroName string, JoinCode string, FacilitatorCode string, maxVotes int, brainstormVisibility string) error
RetroGet(RetroID string, UserID string) (*Retro, error)
RetroGetByUser(UserID string) ([]*Retro, error)
RetroGetByUser(UserID string, Limit int, Offset int) ([]*Retro, int, error)
RetroConfirmFacilitator(RetroID string, userID string) error
RetroGetUsers(RetroID string) []*RetroUser
GetRetroFacilitators(RetroID string) []string
Expand Down
2 changes: 1 addition & 1 deletion thunderdome/storyboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ type StoryboardDataSvc interface {
TeamCreateStoryboard(ctx context.Context, TeamID string, OwnerID string, StoryboardName string, JoinCode string, FacilitatorCode string) (*Storyboard, error)
EditStoryboard(StoryboardID string, StoryboardName string, JoinCode string, FacilitatorCode string) error
GetStoryboard(StoryboardID string, UserID string) (*Storyboard, error)
GetStoryboardsByUser(UserID string) ([]*Storyboard, int, error)
GetStoryboardsByUser(UserID string, Limit int, Offset int) ([]*Storyboard, int, error)
ConfirmStoryboardFacilitator(StoryboardID string, UserID string) error
GetStoryboardUsers(StoryboardID string) []*StoryboardUser
GetStoryboardPersonas(StoryboardID string) []*StoryboardPersona
Expand Down
45 changes: 36 additions & 9 deletions ui/src/pages/retro/Retros.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,45 @@
import { appRoutes } from '../../config';
import LL from '../../i18n/i18n-svelte';
import BoxList from '../../components/BoxList.svelte';
import Pagination from '../../components/global/Pagination.svelte';
export let xfetch;
export let notifications;
export let router;
export let eventTag;
let retros = [];
const retrosPageLimit = 10;
let retroCount = 0;
let retrosPage = 1;
xfetch(`/api/users/${$user.id}/retros`)
.then(res => res.json())
.then(function (bs) {
retros = bs.data;
})
.catch(function () {
notifications.danger($LL.getRetrosErrorMessage());
eventTag('fetch_retros', 'engagement', 'failure');
});
function getRetros() {
const retrosOffset = (retrosPage - 1) * retrosPageLimit;
xfetch(
`/api/users/${$user.id}/retros?limit=${retrosPageLimit}&offset=${retrosOffset}`,
)
.then(res => res.json())
.then(function (result) {
retros = result.data;
retroCount = result.meta.count;
})
.catch(function () {
notifications.danger($LL.getRetrosErrorMessage());
eventTag('fetch_retros', 'engagement', 'failure');
});
}
const changePage = evt => {
retrosPage = evt.detail;
getRetros();
};
onMount(() => {
if (!$user.id) {
router.route(appRoutes.login);
}
getRetros();
});
</script>

Expand All @@ -54,6 +71,16 @@
ownerNameField="teamName"
joinBtnText="{$LL.joinRetro()}"
/>
{#if retroCount > retrosPageLimit}
<div class="mt-6 pt-1 flex justify-center">
<Pagination
bind:current="{retrosPage}"
num_items="{retroCount}"
per_page="{retrosPageLimit}"
on:navigate="{changePage}"
/>
</div>
{/if}
</div>

<div class="w-full md:w-1/2 lg:w-2/5 md:ps-2 xl:ps-4">
Expand Down
44 changes: 35 additions & 9 deletions ui/src/pages/storyboard/Storyboards.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,37 @@
export let eventTag;
let storyboards = [];
const storyboardsPageLimit = 10;
let storyboardCount = 0;
let storyboardsPage = 1;
xfetch(`/api/users/${$user.id}/storyboards`)
.then(res => res.json())
.then(function (bs) {
storyboards = bs.data;
})
.catch(function (error) {
notifications.danger($LL.getStoryboardsErrorMessage());
eventTag('fetch_storyboards', 'engagement', 'failure');
});
function getStoryboards() {
const retrosOffset = (storyboardsPage - 1) * storyboardsPageLimit;
xfetch(
`/api/users/${$user.id}/storyboards?limit=${storyboardsPageLimit}&offset=${retrosOffset}`,
)
.then(res => res.json())
.then(function (result) {
storyboards = result.data;
storyboardCount = result.meta.count;
})
.catch(function (error) {
notifications.danger($LL.getStoryboardsErrorMessage());
eventTag('fetch_storyboards', 'engagement', 'failure');
});
}
const changePage = evt => {
storyboardsPage = evt.detail;
getStoryboards();
};
onMount(() => {
if (!$user.id) {
router.route(appRoutes.login);
}
getStoryboards();
});
</script>

Expand All @@ -53,6 +69,16 @@
pageRoute="{appRoutes.storyboard}"
joinBtnText="{$LL.joinStoryboard()}"
/>
{#if storyboardCount > storyboardsPageLimit}
<div class="mt-6 pt-1 flex justify-center">
<Pagination
bind:current="{storyboardsPage}"
num_items="{storyboardCount}"
per_page="{storyboardsPageLimit}"
on:navigate="{changePage}"
/>
</div>
{/if}
</div>

<div class="w-full md:w-1/2 lg:w-2/5 md:ps-2 xl:ps-4">
Expand Down

0 comments on commit 057291b

Please sign in to comment.