chore: add no floating promises lint rule (#4249)
* chore: add no floating promises lint rule * minor cleanup * fix test by only running if node 22 or greater --------- Co-authored-by: Iain Sproat <68657+iainsproat@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
4371945b71
commit
f76a2c34d3
@@ -46,9 +46,9 @@ const configs = [
|
||||
],
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
'@typescript-eslint/no-unsafe-return': 'error',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/no-misused-promises': 'off', // breaks async middlewares (could be fixed tho)
|
||||
'@typescript-eslint/no-floating-promises': 'off', // too many false positives in knex query builders
|
||||
'@typescript-eslint/restrict-template-expressions': 'off', // too restrictive
|
||||
'@typescript-eslint/no-unsafe-enum-comparison': 'off', // too restrictive
|
||||
'@typescript-eslint/unbound-method': 'off', // too many false positives
|
||||
|
||||
@@ -86,7 +86,7 @@ export const validateStoredAuthCodeFactory =
|
||||
|
||||
// Token is valid, confirm user is authorized to access specified resources.
|
||||
if (resources?.workspaceId) {
|
||||
emit({
|
||||
await emit({
|
||||
eventName: 'workspace.authorized',
|
||||
payload: { userId: payload.userId, workspaceId: resources?.workspaceId }
|
||||
})
|
||||
|
||||
@@ -304,7 +304,7 @@ export const init: SpeckleModule['init'] = async ({ app }) => {
|
||||
])(req, res, next)
|
||||
},
|
||||
async (req, res) => {
|
||||
errorHandler(req, res, async (req, res) => {
|
||||
await errorHandler(req, res, async (req, res) => {
|
||||
const streamId = req.params.streamId
|
||||
const [projectDb, projectStorage] = await Promise.all([
|
||||
getProjectDbClient({ projectId: streamId }),
|
||||
@@ -339,7 +339,7 @@ export const init: SpeckleModule['init'] = async ({ app }) => {
|
||||
await authMiddlewareCreator(createStreamReadPermissions())(req, res, next)
|
||||
},
|
||||
async (req, res) => {
|
||||
errorHandler(req, res, async (req, res) => {
|
||||
await errorHandler(req, res, async (req, res) => {
|
||||
const streamId = req.params.streamId
|
||||
const [projectDb, projectStorage] = await Promise.all([
|
||||
getProjectDbClient({ projectId: streamId }),
|
||||
@@ -378,7 +378,7 @@ export const init: SpeckleModule['init'] = async ({ app }) => {
|
||||
const getBlobMetadataCollection = getBlobMetadataCollectionFactory({
|
||||
db: projectDb
|
||||
})
|
||||
errorHandler(req, res, async (req, res) => {
|
||||
await errorHandler(req, res, async (req, res) => {
|
||||
const blobMetadataCollection = await getBlobMetadataCollection({
|
||||
streamId: req.params.streamId,
|
||||
query: fileName as string
|
||||
|
||||
@@ -45,7 +45,7 @@ const main = async () => {
|
||||
return execution
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
void main().then(() => {
|
||||
// weird TS typing issue
|
||||
yargs.exit(0, undefined as unknown as Error)
|
||||
})
|
||||
|
||||
@@ -66,9 +66,9 @@ export const hasScopes: GraphqlDirectiveBuilder = () => {
|
||||
const currentScopes = context.scopes
|
||||
|
||||
await Promise.all(
|
||||
requiredScopes.map(async (requiredScope: string) => {
|
||||
requiredScopes.map((requiredScope: string) =>
|
||||
validateScopes(currentScopes, requiredScope)
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
const data = await resolve.apply(this, args)
|
||||
|
||||
@@ -46,7 +46,7 @@ export const scheduledCallbackWrapper = async (
|
||||
'The triggered task execution {taskName} failed at {scheduledTime}'
|
||||
)
|
||||
} finally {
|
||||
releaseTaskLock(lock)
|
||||
await releaseTaskLock(lock)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ import { parse, Parser } from 'csv-parse'
|
||||
import { createReadStream } from 'fs'
|
||||
import { createObjectsBatchedAndNoClosuresFactory } from '@/modules/core/services/objects/management'
|
||||
|
||||
const IS_NODE_22_OR_ABOVE = process.versions.node.split('.').map(Number)[0] >= 22
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = legacyGetUserFactory({ db })
|
||||
const requestNewEmailVerification = requestNewEmailVerificationFactory({
|
||||
@@ -99,144 +101,148 @@ describe('Objects streaming REST @core', () => {
|
||||
const ctx = await beforeEachContext()
|
||||
;({ serverAddress } = await initializeTestServer(ctx))
|
||||
})
|
||||
|
||||
it('should close database connections if client connection is prematurely closed', async () => {
|
||||
const userId = await createUser({
|
||||
name: 'emails user',
|
||||
email: createRandomEmail(),
|
||||
password: createRandomPassword()
|
||||
})
|
||||
const user = await getUser(userId)
|
||||
|
||||
const project = {
|
||||
id: '',
|
||||
name: 'test project',
|
||||
ownerId: userId
|
||||
}
|
||||
await createTestStream(project as unknown as BasicTestStream, user)
|
||||
|
||||
const token = `Bearer ${await createPersonalAccessToken(
|
||||
user.id,
|
||||
'test token user A',
|
||||
[
|
||||
Scopes.Streams.Read,
|
||||
Scopes.Streams.Write,
|
||||
Scopes.Users.Read,
|
||||
Scopes.Users.Email,
|
||||
Scopes.Tokens.Write,
|
||||
Scopes.Tokens.Read,
|
||||
Scopes.Profile.Read,
|
||||
Scopes.Profile.Email
|
||||
]
|
||||
)}`
|
||||
|
||||
const manyObjs: { commit: RawSpeckleObject; objs: RawSpeckleObject[] } =
|
||||
generateManyObjects(3333, 'perlin merlin magic')
|
||||
const objsIds = manyObjs.objs.map((o) => o.id)
|
||||
|
||||
await createObjectsBatched({ streamId: project.id, objects: manyObjs.objs })
|
||||
for (let i = 0; i < 4; i++) {
|
||||
forceCloseStreamingConnection({
|
||||
serverAddress,
|
||||
projectId: project.id,
|
||||
token,
|
||||
objsIds
|
||||
;(IS_NODE_22_OR_ABOVE ? it : it.skip)(
|
||||
'should close database connections if client connection is prematurely closed',
|
||||
async () => {
|
||||
const userId = await createUser({
|
||||
name: 'emails user',
|
||||
email: createRandomEmail(),
|
||||
password: createRandomPassword()
|
||||
})
|
||||
}
|
||||
const user = await getUser(userId)
|
||||
|
||||
//sleep for a bit to allow the server to close the connections
|
||||
await new Promise((r) => setTimeout(r, 3000))
|
||||
const gaugeContents = await determineRemainingDatabaseConnectionCapacity({
|
||||
serverAddress
|
||||
})
|
||||
expect(parseInt(gaugeContents), gaugeContents).to.gte(4) //expect all connections to become available again after the client closes them
|
||||
})
|
||||
const project = {
|
||||
id: '',
|
||||
name: 'test project',
|
||||
ownerId: userId
|
||||
}
|
||||
await createTestStream(project as unknown as BasicTestStream, user)
|
||||
|
||||
it('should stream model with some failing feature', async () => {
|
||||
const userId = await createUser({
|
||||
name: 'emails user',
|
||||
email: createRandomEmail(),
|
||||
password: createRandomPassword()
|
||||
})
|
||||
const user = await getUser(userId)
|
||||
const token = `Bearer ${await createPersonalAccessToken(
|
||||
user.id,
|
||||
'test token user A',
|
||||
[
|
||||
Scopes.Streams.Read,
|
||||
Scopes.Streams.Write,
|
||||
Scopes.Users.Read,
|
||||
Scopes.Users.Email,
|
||||
Scopes.Tokens.Write,
|
||||
Scopes.Tokens.Read,
|
||||
Scopes.Profile.Read,
|
||||
Scopes.Profile.Email
|
||||
]
|
||||
)}`
|
||||
|
||||
const project = {
|
||||
id: '',
|
||||
name: 'test project',
|
||||
ownerId: userId
|
||||
}
|
||||
await createTestStream(project as unknown as BasicTestStream, user)
|
||||
const manyObjs: { commit: RawSpeckleObject; objs: RawSpeckleObject[] } =
|
||||
generateManyObjects(3333, 'perlin merlin magic')
|
||||
const objsIds = manyObjs.objs.map((o) => o.id)
|
||||
|
||||
const token = `Bearer ${await createPersonalAccessToken(
|
||||
user.id,
|
||||
'test token user A',
|
||||
[
|
||||
Scopes.Streams.Read,
|
||||
Scopes.Streams.Write,
|
||||
Scopes.Users.Read,
|
||||
Scopes.Users.Email,
|
||||
Scopes.Tokens.Write,
|
||||
Scopes.Tokens.Read,
|
||||
Scopes.Profile.Read,
|
||||
Scopes.Profile.Email
|
||||
]
|
||||
)}`
|
||||
|
||||
// import CSV file
|
||||
const csvStream = createReadStream(
|
||||
//FIXME this relies on running this test from `packages/server` directory
|
||||
`${process.cwd()}/test/assets/failing-streaming-model-f547dc4e88.csv`
|
||||
)
|
||||
// eslint-disable-next-line camelcase
|
||||
.pipe(parse({ delimiter: ',', from_line: 2 }))
|
||||
|
||||
function csvParserAsPromise(
|
||||
stream: Parser
|
||||
): Promise<{ manyObjs: RawSpeckleObject[]; objsIds: string[] }> {
|
||||
const manyObjs: RawSpeckleObject[] = []
|
||||
const objsIds: string[] = []
|
||||
return new Promise((resolve, reject) => {
|
||||
stream.on('data', (row: string[]) => {
|
||||
const obj = JSON.parse(row[1])
|
||||
manyObjs.push(obj)
|
||||
objsIds.push(row[0])
|
||||
await createObjectsBatched({ streamId: project.id, objects: manyObjs.objs })
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await forceCloseStreamingConnection({
|
||||
serverAddress,
|
||||
projectId: project.id,
|
||||
token,
|
||||
objsIds
|
||||
})
|
||||
stream.on('end', () => resolve({ manyObjs, objsIds }))
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
||||
stream.on('error', (error: unknown) => reject(error))
|
||||
}
|
||||
|
||||
//sleep for a bit to allow the server to close the connections
|
||||
await new Promise((r) => setTimeout(r, 3000))
|
||||
const gaugeContents = await determineRemainingDatabaseConnectionCapacity({
|
||||
serverAddress
|
||||
})
|
||||
expect(parseInt(gaugeContents), gaugeContents).to.gte(4) //expect all connections to become available again after the client closes them
|
||||
}
|
||||
|
||||
const { manyObjs, objsIds } = await csvParserAsPromise(csvStream)
|
||||
|
||||
const preGaugeContents = await determineRemainingDatabaseConnectionCapacity({
|
||||
serverAddress
|
||||
})
|
||||
expect(
|
||||
parseInt(preGaugeContents),
|
||||
`Prior to test, we did not have sufficient DB connections free: ${preGaugeContents}`
|
||||
).to.gte(4) // all connections are available before the test
|
||||
|
||||
await createObjectsBatched({ streamId: project.id, objects: manyObjs })
|
||||
for (let i = 0; i < 1; i++) {
|
||||
forceCloseStreamingConnection({
|
||||
serverAddress,
|
||||
projectId: project.id,
|
||||
token,
|
||||
objsIds
|
||||
)
|
||||
;(IS_NODE_22_OR_ABOVE ? it : it.skip)(
|
||||
'should stream model with some failing feature',
|
||||
async () => {
|
||||
const userId = await createUser({
|
||||
name: 'emails user',
|
||||
email: createRandomEmail(),
|
||||
password: createRandomPassword()
|
||||
})
|
||||
}
|
||||
const user = await getUser(userId)
|
||||
|
||||
//sleep for a bit to allow the server to close the connections
|
||||
await new Promise((r) => setTimeout(r, 3000))
|
||||
const postGaugeContents = await determineRemainingDatabaseConnectionCapacity({
|
||||
serverAddress
|
||||
})
|
||||
expect(
|
||||
parseInt(postGaugeContents),
|
||||
`After the test, we did not have sufficient DB connections free: ${postGaugeContents}`
|
||||
).to.gte(4) //expect all connections to become available again after the client closes them
|
||||
}).timeout(50000)
|
||||
const project = {
|
||||
id: '',
|
||||
name: 'test project',
|
||||
ownerId: userId
|
||||
}
|
||||
await createTestStream(project as unknown as BasicTestStream, user)
|
||||
|
||||
const token = `Bearer ${await createPersonalAccessToken(
|
||||
user.id,
|
||||
'test token user A',
|
||||
[
|
||||
Scopes.Streams.Read,
|
||||
Scopes.Streams.Write,
|
||||
Scopes.Users.Read,
|
||||
Scopes.Users.Email,
|
||||
Scopes.Tokens.Write,
|
||||
Scopes.Tokens.Read,
|
||||
Scopes.Profile.Read,
|
||||
Scopes.Profile.Email
|
||||
]
|
||||
)}`
|
||||
|
||||
// import CSV file
|
||||
const csvStream = createReadStream(
|
||||
//FIXME this relies on running this test from `packages/server` directory
|
||||
`${process.cwd()}/test/assets/failing-streaming-model-f547dc4e88.csv`
|
||||
)
|
||||
// eslint-disable-next-line camelcase
|
||||
.pipe(parse({ delimiter: ',', from_line: 2 }))
|
||||
|
||||
function csvParserAsPromise(
|
||||
stream: Parser
|
||||
): Promise<{ manyObjs: RawSpeckleObject[]; objsIds: string[] }> {
|
||||
const manyObjs: RawSpeckleObject[] = []
|
||||
const objsIds: string[] = []
|
||||
return new Promise((resolve, reject) => {
|
||||
stream.on('data', (row: string[]) => {
|
||||
const obj = JSON.parse(row[1])
|
||||
manyObjs.push(obj)
|
||||
objsIds.push(row[0])
|
||||
})
|
||||
stream.on('end', () => resolve({ manyObjs, objsIds }))
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
||||
stream.on('error', (error: unknown) => reject(error))
|
||||
})
|
||||
}
|
||||
|
||||
const { manyObjs, objsIds } = await csvParserAsPromise(csvStream)
|
||||
|
||||
const preGaugeContents = await determineRemainingDatabaseConnectionCapacity({
|
||||
serverAddress
|
||||
})
|
||||
expect(
|
||||
parseInt(preGaugeContents),
|
||||
`Prior to test, we did not have sufficient DB connections free: ${preGaugeContents}`
|
||||
).to.gte(4) // all connections are available before the test
|
||||
|
||||
await createObjectsBatched({ streamId: project.id, objects: manyObjs })
|
||||
for (let i = 0; i < 1; i++) {
|
||||
await forceCloseStreamingConnection({
|
||||
serverAddress,
|
||||
projectId: project.id,
|
||||
token,
|
||||
objsIds
|
||||
})
|
||||
}
|
||||
|
||||
//sleep for a bit to allow the server to close the connections
|
||||
await new Promise((r) => setTimeout(r, 3000))
|
||||
const postGaugeContents = await determineRemainingDatabaseConnectionCapacity({
|
||||
serverAddress
|
||||
})
|
||||
expect(
|
||||
parseInt(postGaugeContents),
|
||||
`After the test, we did not have sufficient DB connections free: ${postGaugeContents}`
|
||||
).to.gte(4) //expect all connections to become available again after the client closes them
|
||||
}
|
||||
).timeout(50000)
|
||||
})
|
||||
|
||||
const forceCloseStreamingConnection = async (params: {
|
||||
|
||||
@@ -437,7 +437,7 @@ describe('Core @user-emails', () => {
|
||||
assertLowercase(updatedEmail)
|
||||
|
||||
randomCaseGuy.email = newEmail
|
||||
updateEmailDirectly(newEmail)
|
||||
await updateEmailDirectly(newEmail)
|
||||
})
|
||||
|
||||
it('with validateAndCreateUserEmailFactory()', async () => {
|
||||
|
||||
@@ -155,12 +155,12 @@ const scheduleWorkspaceTrialExpiry = ({
|
||||
'Workspace trial expired for {workspaceIds}.'
|
||||
)
|
||||
await Promise.all(
|
||||
expiredWorkspacePlans.map(async (plan) => {
|
||||
expiredWorkspacePlans.map(async (plan) =>
|
||||
emit({
|
||||
eventName: 'gatekeeper.workspace-trial-expired',
|
||||
payload: { workspaceId: plan.workspaceId }
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ export const createRenderRequestFactory =
|
||||
id: crs({ length: 10 })
|
||||
})
|
||||
|
||||
deps.publish(ProjectSubscriptions.ProjectVersionGendoAIRenderCreated, {
|
||||
await deps.publish(ProjectSubscriptions.ProjectVersionGendoAIRenderCreated, {
|
||||
projectVersionGendoAIRenderCreated: newRecord
|
||||
})
|
||||
|
||||
@@ -109,7 +109,7 @@ export const updateRenderRequestFactory =
|
||||
input: { ...input, updatedAt: new Date() },
|
||||
id: baseRequest.id
|
||||
})
|
||||
deps.publish(ProjectSubscriptions.ProjectVersionGendoAIRenderUpdated, {
|
||||
await deps.publish(ProjectSubscriptions.ProjectVersionGendoAIRenderUpdated, {
|
||||
projectVersionGendoAIRenderUpdated: record
|
||||
})
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ const isMultiregionJob = (job: Bull.Job): job is Bull.Job<MultiregionJob> => {
|
||||
*/
|
||||
export const startQueue = async () => {
|
||||
const queue = getQueue()
|
||||
queue.process(async (job) => {
|
||||
void queue.process(async (job) => {
|
||||
if (!isMultiregionJob(job)) {
|
||||
throw new MultiRegionInvalidJobError()
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ export function registerNotificationHandlers(
|
||||
*/
|
||||
export async function consumeIncomingNotifications() {
|
||||
const queue = getQueue()
|
||||
queue.process(async (job): Promise<NotificationJobResult> => {
|
||||
void queue.process(async (job): Promise<NotificationJobResult> => {
|
||||
let notificationType: Optional<NotificationType>
|
||||
try {
|
||||
notificationsLogger.info('New notification received...')
|
||||
|
||||
@@ -127,7 +127,7 @@ export const init: SpeckleModule['init'] = ({ app, isInitial, metricsRegister })
|
||||
})
|
||||
app.use(previewRouter)
|
||||
|
||||
previewResponseQueue.process(async (payload, done) => {
|
||||
void previewResponseQueue.process(async (payload, done) => {
|
||||
const parsedMessage = previewResultPayload.safeParse(payload.data)
|
||||
if (!parsedMessage.success) {
|
||||
logger.error(
|
||||
|
||||
@@ -409,7 +409,7 @@ describe('Workspace SSO repositories', () => {
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
truncateTables(['user_sso_sessions'])
|
||||
await truncateTables(['user_sso_sessions'])
|
||||
})
|
||||
|
||||
describe('when deleting an sso provider that exists', async () => {
|
||||
|
||||
@@ -834,7 +834,7 @@ describe('Workspaces GQL CRUD', () => {
|
||||
authorId: ''
|
||||
}
|
||||
|
||||
createTestCommit(testVersion, {
|
||||
await createTestCommit(testVersion, {
|
||||
owner: testAdminUser,
|
||||
stream: workspaceProject
|
||||
})
|
||||
|
||||
@@ -191,7 +191,7 @@ export const initKnexPrometheusMetrics = async (params: {
|
||||
// configure hooks on knex
|
||||
for (const dbClient of await params.getAllDbClients()) {
|
||||
if (initializedRegions.includes(dbClient.regionKey)) continue
|
||||
initKnexPrometheusMetricsForRegionEvents({
|
||||
await initKnexPrometheusMetricsForRegionEvents({
|
||||
logger: params.logger,
|
||||
region: dbClient.regionKey,
|
||||
db: dbClient.client
|
||||
|
||||
Reference in New Issue
Block a user