Fe2 server management bugfixes (#1787)

* fix(server): inviteList pagination

* Fixes from call with fabians

* more BE bufxies

* reducing server invite precision

* Infinite Scroll fixes. Slight design change to "update available"

* fixed tests

---------

Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com>
This commit is contained in:
andrewwallacespeckle
2023-09-08 12:55:03 +01:00
committed by GitHub
parent d95d03605e
commit 44bfa6d2c8
10 changed files with 119 additions and 62 deletions
@@ -12,6 +12,10 @@ import { expect } from 'chai'
import { ApolloServer } from 'apollo-server-express'
import { ServerInviteRecord } from '@/modules/serverinvites/helpers/types'
import { Optional } from '@/modules/shared/helpers/typeHelper'
import { wait } from '@speckle/shared'
// To ensure that the invites are created in the correct order, we need to wait a bit between each creation
const WAIT_TIMEOUT = 5
function randomEl<T>(array: T[]): T {
return array[Math.floor(Math.random() * array.length)]
@@ -90,18 +94,18 @@ describe('[Admin users list]', () => {
let remainingSearchQueryInviteCount = SEARCH_QUERY_RESULT_COUNT
// Create Users
await Promise.all(
// count - 1, cause `me` also exists
times(USER_COUNT - 1, (i) =>
createUser({
name: `User #${i} - ${
remainingSearchQueryUserCount-- >= 1 ? SEARCH_QUERY : ''
}`,
email: `speckleuser${i}@gmail.com`,
password: 'sn3aky-1337-b1m'
}).then((id) => userIds.push(id))
)
)
// count - 1, cause `me` also exists
for (let i = 0; i < USER_COUNT - 1; i++) {
const id = await createUser({
name: `User #${i} - ${
remainingSearchQueryUserCount-- >= 1 ? SEARCH_QUERY : ''
}`,
email: `speckleuser${i}@gmail.com`,
password: 'sn3aky-1337-b1m'
})
userIds.push(id)
await wait(WAIT_TIMEOUT)
}
// Create streams
const streamData: { id: string; ownerId: string }[] = []
@@ -117,34 +121,35 @@ describe('[Admin users list]', () => {
)
// Create invites
await Promise.all([
// Server invites
...times(SERVER_INVITE_COUNT, (i) =>
createInviteDirectly(
{
email: `randominvitee${i}.${
remainingSearchQueryInviteCount-- >= 1 ? SEARCH_QUERY : ''
}@gmail.com`
},
randomEl(userIds)
)
),
// Stream invites
...times(STREAM_INVITE_COUNT, (i) => {
const { id: streamId, ownerId } = randomEl(streamData)
const email = `streamrandominvitee${i}.${
remainingSearchQueryInviteCount-- >= 1 ? SEARCH_QUERY : ''
}@gmail.com`
// Server invites
for (let i = 0; i < SERVER_INVITE_COUNT; i++) {
await createInviteDirectly(
{
email: `randominvitee${i}.${
remainingSearchQueryInviteCount-- >= 1 ? SEARCH_QUERY : ''
}@gmail.com`
},
randomEl(userIds)
)
await wait(WAIT_TIMEOUT)
}
return createInviteDirectly(
{
streamId,
email
},
ownerId
)
})
])
// Stream invites
for (let i = 0; i < STREAM_INVITE_COUNT; i++) {
const { id: streamId, ownerId } = randomEl(streamData)
const email = `streamrandominvitee${i}.${
remainingSearchQueryInviteCount-- >= 1 ? SEARCH_QUERY : ''
}@gmail.com`
await createInviteDirectly(
{
streamId,
email
},
ownerId
)
await wait(WAIT_TIMEOUT)
}
// Create a few more stream invites to registered users, which should not appear in
// the users list
@@ -0,0 +1,31 @@
import { Knex } from 'knex'
/**
* MIGRATING STREAMS TIMESTAMP FIELDS TO A LOWER PRECISION, CAUSE JS CANT HANDLE
* IT BEING THAT HIGH AND THIS GENERATES BUGS
*/
const TABLE_NAME = 'server_invites'
const TIMESTAMP_COLUMNS = ['createdAt']
export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable(TABLE_NAME, (table) => {
TIMESTAMP_COLUMNS.forEach((col) => {
table
.timestamp(col, { precision: 3, useTz: true })
.defaultTo(knex.fn.now())
.alter()
})
})
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TABLE_NAME, (table) => {
TIMESTAMP_COLUMNS.forEach((col) => {
table
.timestamp(col, { precision: 6, useTz: true })
.defaultTo(knex.fn.now())
.alter()
})
})
}
@@ -13,7 +13,7 @@ const { getStream } = require('@/modules/core/repositories/streams')
/**
* Use this wherever you're retrieving invites, not necessarily where you're writing to them
*/
const getInvitesBaseQuery = () => {
const getInvitesBaseQuery = (sort = 'asc') => {
const q = ServerInvites.knex().select(ServerInvites.cols)
// join just to ensure we don't retrieve invalid invites
@@ -25,7 +25,7 @@ const getInvitesBaseQuery = () => {
w1.whereNull(ServerInvites.col.resourceId).orWhereNotNull(Streams.col.id)
})
q.orderBy(ServerInvites.col.createdAt)
q.orderBy(ServerInvites.col.createdAt, sort)
return q
}
@@ -225,8 +225,8 @@ async function deleteStreamInvite(inviteId) {
.delete()
}
function findServerInvitesBaseQuery(searchQuery) {
const q = getInvitesBaseQuery()
function findServerInvitesBaseQuery(searchQuery, sort) {
const q = getInvitesBaseQuery(sort)
if (searchQuery) {
// TODO: Is this safe from SQL injection?
@@ -272,10 +272,9 @@ async function findServerInvites(searchQuery, limit, offset) {
* @returns {Promise<ServerInviteRecord[]>}
*/
async function queryServerInvites(searchQuery, limit, cursor) {
const q = findServerInvitesBaseQuery(searchQuery)
.limit(limit)
.orderBy(ServerInvites.col.createdAt, 'desc')
if (cursor) q.where(ServerInvites.col.createdAt, '<', cursor)
const q = findServerInvitesBaseQuery(searchQuery, 'desc').limit(limit)
if (cursor) q.where(ServerInvites.col.createdAt, '<', cursor.toISOString())
return await q
}