Skip to content

Commit

Permalink
feat: Add lists updated at column (#138)
Browse files Browse the repository at this point in the history
* feat: Add Update List endpoint

* fix: If the name or description are not set avoid trying to update them

* test: Test cases in which access utils should not throw errors

* test: Add test to check if the update list is being called with the correct params from the handler

* test: Fix tests after merging master

* refactor: Move queries to a new file called queries instead of utils

* fix: Remove util import for debugging purposes

* chore: Uninstall lodash

* fix: Manually append updatable columns to the update list query

* feat: Add migration

* feat: Add validations for name and description in the create list handler

* feat: When creating a new list, add the value of the description if coming from the UI

* feat: Add length validations and generalize create and update handler validations

* feat: Create lists support private property

* feat: Add schema validator component

* feat: Use schema validator for list creation

* feat: Use schema validator for list updates

* test: Fix & add tests for the new transaction mechanism

* feat: Add withTransaction fn to the pg component to generalize the bg, cm, rb, and release behaviors

* refactor: Allow async operations in the withTransaction onError parameter

* test: Remove assertions related to the new withTransaction fn

* test: Test the extension of the pg component

* refactor: Use the new implementation of the withTransaction for updating list

* refactor: Use the new withTransaction implementation for the addPickToList mechanism

* refactor: It's not necesary to check for the AccessNotFound error when creating a new list

* refactor: Avoid raising unneeded errors related to the acl when updating a list

* feat: Alter lists adding a new column updated_at

* feat: Return the updated at prop when getting a list

* feat: Allow users to sort lists by updated at

* feat: If the list to retrieve is the default use as the updated_at the last pick date

* feat: Add triggers to update the updated at when inserting or deleting picks or permissions

* fix: Fix failing tests applying the last PR changes to the updated at sorting

* Update test/unit/lists-component.spec.ts

Co-authored-by: Lautaro Petaccio <1120791+LautaroPetaccio@users.noreply.github.com>
Signed-off-by: Kevin Szuchet <31735779+kevinszuchet@users.noreply.github.com>

---------

Signed-off-by: Kevin Szuchet <31735779+kevinszuchet@users.noreply.github.com>
Co-authored-by: Lautaro Petaccio <1120791+LautaroPetaccio@users.noreply.github.com>
  • Loading branch information
kevinszuchet and LautaroPetaccio committed May 30, 2023
1 parent c2aac50 commit 89641c6
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 57 deletions.
1 change: 1 addition & 0 deletions src/adapters/lists/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export function fromDBListToList(dbList: DBList): List {
description: dbList.description,
userAddress: dbList.user_address,
createdAt: dbList.created_at,
updatedAt: dbList.updated_at,
permission: dbList.permission as Permission
}
}
Expand Down
1 change: 1 addition & 0 deletions src/adapters/lists/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type List = {
description: string | null
userAddress: string
createdAt: Date
updatedAt: Date | null
permission?: Permission | null
}

Expand Down
2 changes: 1 addition & 1 deletion src/controllers/handlers/lists-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ export async function getListsHandler(
status: StatusCode.BAD_REQUEST,
body: {
ok: false,
message: 'The sort by parameter is not defined as date or name.'
message: 'The sort by parameter is not defined as createdAt, name, or updatedAt.'
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/migrations/1683320488882_acl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MigrationBuilder, ColumnDefinitions } from 'node-pg-migrate'
import { LISTS_TABLE } from './1677778846950_lists-and-picks'

export const shorthands: ColumnDefinitions | undefined = undefined
const ACL_TABLE = 'acl'
export const ACL_TABLE = 'acl'
const PERMISSION_TYPE = 'permissions'

export async function up(pgm: MigrationBuilder): Promise<void> {
Expand Down
41 changes: 41 additions & 0 deletions src/migrations/1685096522768_add-column-updated-at-to-lists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { MigrationBuilder, ColumnDefinitions } from 'node-pg-migrate'
import { LISTS_TABLE } from './1677778846950_lists-and-picks'

export const shorthands: ColumnDefinitions | undefined = undefined

const FUNCTION_NAME = 'update_lists_updated_at'
const TRIGGER_NAME = 'trigger_update_lists_updated_at'

export async function up(pgm: MigrationBuilder): Promise<void> {
pgm.addColumns(LISTS_TABLE, {
updated_at: { type: 'timestamp', default: null }
})
pgm.createIndex(LISTS_TABLE, 'updated_at')
pgm.createFunction(
FUNCTION_NAME,
[],
{
returns: 'TRIGGER',
language: 'plpgsql',
replace: true
},
`BEGIN
NEW.updated_at := NOW();
RETURN NEW;
END;`
)
pgm.createTrigger(LISTS_TABLE, TRIGGER_NAME, {
when: 'BEFORE',
operation: 'UPDATE',
function: FUNCTION_NAME,
level: 'ROW'
})
}

export async function down(pgm: MigrationBuilder): Promise<void> {
pgm.dropTrigger(LISTS_TABLE, TRIGGER_NAME)
pgm.dropFunction(FUNCTION_NAME, [])
pgm.dropIndex(LISTS_TABLE, 'updated_at')
pgm.dropColumn(LISTS_TABLE, 'updated_at')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { MigrationBuilder, ColumnDefinitions } from 'node-pg-migrate'
import { PICKS_TABLE } from './1677778846950_lists-and-picks'
import { ACL_TABLE } from './1683320488882_acl'

export const shorthands: ColumnDefinitions | undefined = undefined

const ON_INSERT_FUNCTION_NAME = 'update_lists_updated_at_when_inserting_related_rows'
const ON_DELETE_FUNCTION_NAME = 'update_lists_updated_at_when_deleting_related_rows'

export async function up(pgm: MigrationBuilder): Promise<void> {
pgm.createFunction(
ON_INSERT_FUNCTION_NAME,
[],
{
returns: 'TRIGGER',
language: 'plpgsql',
replace: true
},
`BEGIN
UPDATE favorites.lists
SET updated_at = NOW()
WHERE id = NEW.list_id;
RETURN NEW;
END;`
)

pgm.createFunction(
ON_DELETE_FUNCTION_NAME,
[],
{
returns: 'TRIGGER',
language: 'plpgsql',
replace: true
},
`BEGIN
UPDATE favorites.lists
SET updated_at = NOW()
WHERE id = OLD.list_id;
RETURN NULL;
END;`
)

const tables = [PICKS_TABLE, ACL_TABLE]
const functions = [
{ operation: 'INSERT', name: ON_INSERT_FUNCTION_NAME },
{ operation: 'DELETE', name: ON_DELETE_FUNCTION_NAME }
]

tables.forEach(tableName => {
functions.forEach(({ operation, name: functionName }) => {
pgm.createTrigger(tableName, `trigger_update_lists_updated_at_on_${operation.toLowerCase()}`, {
when: 'AFTER',
operation,
function: functionName,
level: 'ROW'
})
})
})
}

export async function down(pgm: MigrationBuilder): Promise<void> {
pgm.dropTrigger(PICKS_TABLE, 'trigger_update_lists_updated_at_on_insert')
pgm.dropTrigger(PICKS_TABLE, 'trigger_update_lists_updated_at_on_delete')
pgm.dropTrigger(ACL_TABLE, 'trigger_update_lists_updated_at_on_insert')
pgm.dropTrigger(ACL_TABLE, 'trigger_update_lists_updated_at_on_delete')
pgm.dropFunction(ON_INSERT_FUNCTION_NAME, [])
pgm.dropFunction(ON_DELETE_FUNCTION_NAME, [])
}
3 changes: 3 additions & 0 deletions src/ports/lists/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ export function createListsComponent(
case ListSortBy.CREATED_AT:
orderByQuery.append(`, l.created_at ${sortDirectionKeyword}`)
break
case ListSortBy.UPDATED_AT:
orderByQuery.append(`, l.updated_at ${sortDirectionKeyword}`)
break
case ListSortBy.NAME:
orderByQuery.append(`, l.name ${sortDirectionKeyword}`)
break
Expand Down
11 changes: 8 additions & 3 deletions src/ports/lists/queries.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import SQL from 'sql-template-strings'
import { DEFAULT_LIST_USER_ADDRESS } from '../../migrations/1678303321034_default-list'
import { DEFAULT_LIST_ID, DEFAULT_LIST_USER_ADDRESS } from '../../migrations/1678303321034_default-list'
import { Permission } from '../access'
import { GRANTED_TO_ALL } from './constants'
import { GetListOptions } from './types'

export function getListQuery(listId: string, { requiredPermission, considerDefaultList = true, userAddress }: GetListOptions) {
const query = SQL`
SELECT favorites.lists.*, favorites.acl.permission AS permission, COUNT(favorites.picks.item_id) AS count_items
const query = SQL`SELECT favorites.lists.id, favorites.lists.name, favorites.lists.description, favorites.lists.user_address, favorites.lists.created_at`

query.append(listId === DEFAULT_LIST_ID ? SQL`MAX(favorites.picks.created_at) as updated_at` : SQL`, favorites.lists.updated_at`)

query.append(
SQL`, favorites.acl.permission AS permission, COUNT(favorites.picks.item_id) AS count_items
FROM favorites.lists
LEFT JOIN favorites.picks ON favorites.lists.id = favorites.picks.list_id AND favorites.picks.user_address = ${userAddress}
LEFT JOIN favorites.acl ON favorites.lists.id = favorites.acl.list_id`
)

query.append(SQL` WHERE favorites.lists.id = ${listId} AND (favorites.lists.user_address = ${userAddress}`)
if (considerDefaultList) {
Expand Down
4 changes: 3 additions & 1 deletion src/ports/lists/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type DBList = {
description: string | null
user_address: string
created_at: Date
updated_at: Date | null
permission?: string | null
}

Expand All @@ -62,7 +63,8 @@ export type NewList = AddListRequestBody & {

export enum ListSortBy {
CREATED_AT = 'createdAt',
NAME = 'name'
NAME = 'name',
UPDATED_AT = 'updatedAt'
}

export enum ListSortDirection {
Expand Down
9 changes: 8 additions & 1 deletion test/unit/lists-adapters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ describe('when transforming DB retrieved lists to lists with count', () => {
description: 'Super description of list #1',
user_address: '0x45abb534BD927284F84b03d43f33dF0E5C91C21f',
created_at: new Date(),
updated_at: new Date(),
lists_count: '3',
items_count: '5',
is_item_in_list: true
Expand All @@ -130,6 +131,7 @@ describe('when transforming DB retrieved lists to lists with count', () => {
description: 'Super description of list #2',
user_address: '0x45abb534BD927284F84b03d43f33dF0E5C91C21f',
created_at: new Date(),
updated_at: new Date(),
lists_count: '3',
items_count: '4',
is_item_in_list: false
Expand All @@ -140,6 +142,7 @@ describe('when transforming DB retrieved lists to lists with count', () => {
description: 'Super description of list #3',
user_address: '0x45abb534BD927284F84b03d43f33dF0E5C91C21f',
created_at: new Date(),
updated_at: new Date(),
lists_count: '3',
items_count: '2'
}
Expand Down Expand Up @@ -170,7 +173,8 @@ describe('when transforming a DB retrieved list to a list', () => {
name: 'List #1',
user_address: '0x45abb534BD927284F84b03d43f33dF0E5C91C21f',
description: 'This is a list',
created_at: date
created_at: date,
updated_at: date
}
})

Expand All @@ -181,6 +185,7 @@ describe('when transforming a DB retrieved list to a list', () => {
userAddress: dbList.user_address,
description: dbList.description,
createdAt: date,
updatedAt: date,
permission: undefined
})
})
Expand All @@ -197,6 +202,7 @@ describe('when transforming a DB retrieved list with items count to a list with
user_address: '0x45abb534BD927284F84b03d43f33dF0E5C91C21f',
description: 'This is a list',
created_at: date,
updated_at: date,
items_count: '5'
}
})
Expand All @@ -208,6 +214,7 @@ describe('when transforming a DB retrieved list with items count to a list with
userAddress: dbListWithItemsCount.user_address,
description: dbListWithItemsCount.description,
createdAt: date,
updatedAt: date,
permission: undefined,
itemsCount: 5
})
Expand Down
Loading

0 comments on commit 89641c6

Please sign in to comment.