From acd41eb0be90933db09af6b02d3ffe0c56a64efd Mon Sep 17 00:00:00 2001 From: Pascal Jufer Date: Thu, 29 Apr 2021 18:11:43 +0200 Subject: [PATCH] Syntax fixes (#5367) * Declare return types on functions And a very few other type related minor fixes * Minor syntax fixes * Remove unnecessary escape chars in regexes * Remove unnecessary awaits * Replace deprecated req.connection with req.socket * Replace deprecated upload with uploadOne * Remove unnecessary eslint-disable-next-line comments * Comment empty functions / catch or finally clauses * Fix irregular whitespaces * Add missing returns (null) * Remove unreachable code * A few logical fixes * Remove / Handle non-null assertions which are certainly unnecessary (e.g. in tests) --- api/src/app.ts | 2 +- api/src/cli/commands/bootstrap/index.ts | 2 +- api/src/cli/commands/count/index.ts | 2 +- api/src/cli/commands/database/install.ts | 2 +- api/src/cli/commands/database/migrate.ts | 2 +- api/src/cli/commands/init/index.ts | 4 +- api/src/cli/commands/init/questions.ts | 14 +++---- api/src/cli/commands/roles/create.ts | 2 +- api/src/cli/commands/users/create.ts | 2 +- api/src/cli/commands/users/passwd.ts | 2 +- api/src/cli/utils/create-db-connection.ts | 2 +- api/src/cli/utils/create-env/index.ts | 6 ++- api/src/controllers/auth.ts | 4 +- api/src/database/index.ts | 6 +-- ...0201028A-remove-collection-foreign-keys.ts | 4 +- .../20201029A-remove-system-relations.ts | 4 +- .../20201029B-remove-system-collections.ts | 4 +- .../20201029C-remove-system-fields.ts | 4 +- .../20201105A-add-cascade-system-relations.ts | 4 +- .../20201105B-change-webhook-url-type.ts | 4 +- .../20210225A-add-relations-sort-field.ts | 4 +- .../20210304A-remove-locked-fields.ts | 4 +- .../20210312A-webhooks-collections-text.ts | 4 +- .../20210331A-add-refresh-interval.ts | 4 +- .../20210415A-make-filesize-nullable.ts | 4 +- ...0210416A-add-collections-accountability.ts | 4 +- .../20210422A-remove-files-interface.ts | 6 ++- api/src/database/migrations/run.ts | 2 +- api/src/database/seeds/run.ts | 2 +- api/src/emitter.ts | 2 +- api/src/exceptions/database/dialects/mssql.ts | 26 ++++-------- api/src/exceptions/database/dialects/mysql.ts | 31 +++++--------- .../exceptions/database/dialects/oracle.ts | 2 +- .../exceptions/database/dialects/postgres.ts | 19 ++------- .../exceptions/database/dialects/sqlite.ts | 9 +---- api/src/exceptions/database/dialects/types.ts | 40 +++++++++++++++++++ api/src/exceptions/database/translate.ts | 11 ++--- api/src/extensions.ts | 12 +++--- api/src/middleware/use-collection.ts | 3 +- api/src/rate-limiter.ts | 3 +- api/src/server.ts | 20 +++++----- api/src/services/assets.ts | 8 +++- api/src/services/authentication.ts | 14 ++++--- api/src/services/authorization.ts | 2 +- api/src/services/collections.ts | 5 ++- api/src/services/fields.ts | 14 ++++--- api/src/services/files.ts | 10 ++--- api/src/services/graphql.ts | 37 +++++++++++------ api/src/services/items.ts | 6 +-- api/src/services/meta.ts | 6 +-- api/src/services/payload.ts | 7 +++- api/src/services/permissions.ts | 2 +- api/src/services/revisions.ts | 2 +- api/src/services/server.ts | 4 +- api/src/services/users.ts | 12 +++--- api/src/services/utils.ts | 2 +- api/src/start.ts | 2 +- api/src/types/deep-partial.d.ts | 3 ++ api/src/types/express-session.d.ts | 4 +- api/src/utils/apply-query.ts | 6 +-- api/src/utils/deep-map.ts | 1 + api/src/utils/filter-items.ts | 2 +- api/src/utils/get-cache-key.ts | 2 +- api/src/utils/get-config-from-env.ts | 2 +- api/src/utils/get-default-value.ts | 4 +- api/src/utils/get-email-from-profile.ts | 2 +- api/src/utils/get-graphql-type.ts | 4 +- api/src/utils/is-jwt.ts | 2 +- api/src/utils/list-folders.ts | 2 +- api/src/utils/parse-filter.ts | 2 +- api/src/utils/parse-iptc.ts | 2 +- api/src/utils/reduce-schema.ts | 2 +- api/src/utils/require-yaml.ts | 2 +- api/src/utils/sanitize-query.ts | 2 +- api/src/utils/track.ts | 2 +- api/src/utils/validate-env.ts | 2 +- api/src/utils/validate-query.ts | 2 +- api/src/webhooks.ts | 4 +- app/src/api.ts | 10 ++--- app/src/auth.ts | 6 +-- .../expand/transition-expand-methods.ts | 2 +- .../v-form/form-field-interface.vue | 5 ++- app/src/components/v-form/v-form.vue | 4 +- app/src/components/v-menu/use-popper.ts | 2 +- app/src/components/v-slider/v-slider.vue | 2 +- app/src/components/v-table/v-table.vue | 2 +- app/src/composables/groupable/groupable.ts | 19 +++++---- app/src/composables/size-class/size-class.ts | 4 +- .../unsaved-changes/unsaved-changes.ts | 2 +- .../use-collection/use-collection.ts | 7 ++-- .../use-custom-selection.ts | 14 +++++-- .../use-element-size/use-element-size.ts | 4 +- .../use-event-listener/use-event-listener.ts | 2 +- .../use-field-tree/use-field-tree.ts | 4 +- .../use-form-fields/use-form-fields.ts | 9 +++-- app/src/composables/use-item/use-item.ts | 2 +- app/src/composables/use-items/use-items.ts | 2 +- app/src/composables/use-permissions.ts | 8 +++- app/src/composables/use-preset/use-preset.ts | 7 +++- .../use-scroll-distance.ts | 2 +- .../composables/use-shortcut/use-shortcut.ts | 10 ++--- app/src/composables/use-template-data.ts | 5 ++- .../use-time-from-now/use-time-from-now.ts | 4 +- app/src/composables/use-title/use-title.ts | 2 +- .../use-window-size/use-window-size.ts | 4 +- .../directives/click-outside/click-outside.ts | 8 ++-- app/src/directives/tooltip/tooltip.ts | 14 +++---- app/src/displays/filesize/handler.ts | 2 +- app/src/displays/index.ts | 2 +- app/src/displays/register.ts | 5 ++- app/src/displays/related-values/index.ts | 4 +- app/src/displays/related-values/options.vue | 2 + .../related-values/related-values.vue | 3 +- app/src/displays/types.ts | 5 ++- app/src/hydrate.ts | 6 +-- .../interface-options/interface-options.vue | 3 +- .../_system/interface/interface.vue | 10 +++-- app/src/interfaces/code/code.vue | 10 +++-- app/src/interfaces/image/image.vue | 2 +- app/src/interfaces/index.ts | 2 +- .../interfaces/m2a-builder/m2a-builder.vue | 1 - .../interfaces/many-to-many/many-to-many.vue | 3 +- .../interfaces/many-to-many/use-actions.ts | 2 +- app/src/interfaces/many-to-many/use-edit.ts | 2 +- .../interfaces/many-to-many/use-preview.ts | 2 +- .../interfaces/many-to-many/use-relation.ts | 2 +- .../interfaces/many-to-many/use-selection.ts | 2 +- app/src/interfaces/many-to-many/use-sort.ts | 2 +- .../markdown/composables/use-edit.ts | 2 +- app/src/interfaces/markdown/markdown.vue | 2 +- app/src/interfaces/register.ts | 5 ++- app/src/interfaces/repeater/options.vue | 2 +- .../interfaces/wysiwyg/get-editor-styles.ts | 2 +- app/src/interfaces/wysiwyg/useImage.ts | 2 +- app/src/interfaces/wysiwyg/useLink.ts | 2 +- app/src/interfaces/wysiwyg/useMedia.ts | 2 +- app/src/interfaces/wysiwyg/useSourceCode.ts | 2 +- app/src/lang/index.ts | 4 +- app/src/layouts/cards/cards.vue | 4 +- app/src/layouts/index.ts | 2 +- app/src/layouts/register.ts | 2 +- app/src/layouts/tabular/tabular.vue | 16 ++++---- app/src/layouts/types.ts | 2 +- .../collections/composables/use-navigation.ts | 2 +- .../collections/composables/use-search.ts | 4 +- app/src/modules/docs/components/markdown.vue | 2 +- app/src/modules/docs/routes/static.vue | 2 + .../modules/files/components/navigation.vue | 6 +-- .../modules/files/composables/use-folders.ts | 6 +-- app/src/modules/files/routes/collection.vue | 4 +- app/src/modules/index.ts | 2 +- app/src/modules/register.ts | 6 +-- .../settings/composables/use-project-info.ts | 4 +- .../field-detail/components/display.vue | 10 +++-- .../field-detail/components/interface.vue | 3 +- .../field-detail/components/tabs.vue | 2 +- .../routes/data-model/field-detail/store.ts | 12 +++--- .../fields/components/field-select.vue | 3 +- .../roles/item/composables/use-permissions.ts | 18 +-------- .../composables/use-update-permissions.ts | 2 +- .../users/composables/use-navigation.ts | 2 +- app/src/modules/users/routes/collection.vue | 3 +- app/src/router.ts | 6 ++- .../routes/accept-invite/accept-invite.vue | 4 -- app/src/shims.d.ts | 3 ++ app/src/utils/abbreviate-number.ts | 2 +- app/src/utils/add-query-to-path.ts | 2 +- .../adjust-fields-for-displays.ts | 5 ++- .../arrays-are-equal/arrays-are-equal.ts | 2 +- app/src/utils/deep-map.ts | 3 +- .../filters-to-query/filters-to-query.ts | 2 +- .../utils/format-filesize/format-filesize.ts | 2 +- app/src/utils/generate-joi/index.ts | 2 +- .../get-date-fns-locale.ts | 2 +- .../get-default-interface-for-type.ts | 2 +- app/src/utils/get-endpoint.ts | 2 +- app/src/utils/get-fields-from-template.ts | 2 +- app/src/utils/get-js-type.ts | 2 +- .../get-related-collection.ts | 3 +- app/src/utils/get-root-path.ts | 2 +- .../utils/hide-drag-image/hide-drag-image.ts | 2 +- app/src/utils/hljs-graphql.ts | 2 +- app/src/utils/is-allowed.ts | 2 +- app/src/utils/jwt-payload/jwt-payload.ts | 2 +- app/src/utils/move-in-array/move-in-array.ts | 2 +- app/src/utils/notify.ts | 2 +- app/src/utils/parse-filter.ts | 2 +- .../readable-mime-type/readable-mime-type.ts | 2 +- .../register-component/register-component.ts | 3 +- app/src/utils/render-string-template.ts | 15 +++++-- app/src/utils/set-favicon/set-favicon.ts | 19 ++++----- app/src/utils/translate-object-values.ts | 2 +- .../translate-shortcut/translate-shortcut.ts | 2 +- app/src/utils/unexpected-error.ts | 2 +- app/src/utils/upload-file/upload-file.ts | 2 +- app/src/utils/upload-files/upload-files.ts | 2 +- .../components/drawer-item/drawer-item.vue | 2 +- .../get-available-operators-for-type.ts | 5 ++- .../components/filter-sidebar-detail/types.ts | 5 +++ .../components/image-editor/image-editor.vue | 1 - .../latency-indicator/latency-indicator.vue | 6 ++- .../components/module-bar/module-bar.vue | 5 ++- .../render-display/render-display.vue | 5 ++- .../render-template/render-template.vue | 5 ++- .../components/user-popover/user-popover.vue | 1 + app/vue.config.js | 3 -- .../drive-azure/src/AzureBlobWebServices.ts | 2 +- packages/drive-gcs/src/GoogleCloudStorage.ts | 8 ++-- .../src/AmazonWebServicesS3Storage.ts | 3 +- packages/drive/src/LocalFileSystemStorage.ts | 6 +-- packages/drive/src/utils.ts | 2 +- .../tests/LocalFileSystemStorage.test.ts | 2 - packages/drive/tests/StorageManager.test.ts | 2 +- packages/schema/src/dialects/mysql.ts | 2 +- packages/schema/src/dialects/oracledb.ts | 2 +- packages/schema/src/dialects/postgres.ts | 2 +- packages/schema/src/dialects/sqlite.ts | 2 +- packages/sdk/src/base/auth.ts | 12 ++++-- packages/sdk/src/handlers/activity.ts | 2 +- packages/sdk/src/handlers/extensions.ts | 2 +- packages/sdk/src/handlers/graphql.ts | 2 + packages/sdk/tests/base/storage/tests.ts | 2 +- .../sdk/tests/base/transport/axios.test.ts | 10 ++--- packages/sdk/tests/handlers/utils.test.ts | 6 +-- packages/sdk/tests/items.test.ts | 34 ++++++++-------- packages/sdk/tests/singleton.test.ts | 12 +++--- packages/sdk/tests/utils.ts | 4 +- tests/setup/global.ts | 16 ++++++++ tests/setup/setup.ts | 15 ++----- tests/setup/teardown.ts | 9 +---- tests/setup/utils/await-connection.ts | 8 +++- 231 files changed, 646 insertions(+), 527 deletions(-) create mode 100644 api/src/exceptions/database/dialects/types.ts create mode 100644 tests/setup/global.ts diff --git a/api/src/app.ts b/api/src/app.ts index 078520aced..36022443b2 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -51,7 +51,7 @@ import { emitAsyncSafe } from './emitter'; import fse from 'fs-extra'; -export default async function createApp() { +export default async function createApp(): Promise { validateEnv(['KEY', 'SECRET']); await validateDBConnection(); diff --git a/api/src/cli/commands/bootstrap/index.ts b/api/src/cli/commands/bootstrap/index.ts index cc24c03854..59e6efcd8f 100644 --- a/api/src/cli/commands/bootstrap/index.ts +++ b/api/src/cli/commands/bootstrap/index.ts @@ -5,7 +5,7 @@ import runMigrations from '../../../database/migrations/run'; import { getSchema } from '../../../utils/get-schema'; import { nanoid } from 'nanoid'; -export default async function bootstrap() { +export default async function bootstrap(): Promise { logger.info('Initializing bootstrap...'); if ((await isDatabaseAvailable()) === false) { diff --git a/api/src/cli/commands/count/index.ts b/api/src/cli/commands/count/index.ts index c86a0da51f..c19d4cd603 100644 --- a/api/src/cli/commands/count/index.ts +++ b/api/src/cli/commands/count/index.ts @@ -1,4 +1,4 @@ -export default async function count(collection: string) { +export default async function count(collection: string): Promise { const database = require('../../../database/index').default; if (!collection) { diff --git a/api/src/cli/commands/database/install.ts b/api/src/cli/commands/database/install.ts index c7c8abff0c..24e52e669c 100644 --- a/api/src/cli/commands/database/install.ts +++ b/api/src/cli/commands/database/install.ts @@ -2,7 +2,7 @@ import { Knex } from 'knex'; import installSeeds from '../../../database/seeds/run'; import runMigrations from '../../../database/migrations/run'; -export default async function start() { +export default async function start(): Promise { const database = require('../../../database/index').default as Knex; try { diff --git a/api/src/cli/commands/database/migrate.ts b/api/src/cli/commands/database/migrate.ts index 8d51359c3e..97b87e812c 100644 --- a/api/src/cli/commands/database/migrate.ts +++ b/api/src/cli/commands/database/migrate.ts @@ -1,6 +1,6 @@ import run from '../../../database/migrations/run'; -export default async function migrate(direction: 'latest' | 'up' | 'down') { +export default async function migrate(direction: 'latest' | 'up' | 'down'): Promise { const database = require('../../../database').default; try { diff --git a/api/src/cli/commands/init/index.ts b/api/src/cli/commands/init/index.ts index 23ef86a4bc..f46d342a1e 100644 --- a/api/src/cli/commands/init/index.ts +++ b/api/src/cli/commands/init/index.ts @@ -15,7 +15,7 @@ import runMigrations from '../../../database/migrations/run'; import createDBConnection, { Credentials } from '../../utils/create-db-connection'; import { Knex } from 'knex'; -export default async function init(options: Record) { +export default async function init(): Promise { const rootPath = process.cwd(); let { client } = await inquirer.prompt([ @@ -39,7 +39,7 @@ export default async function init(options: Record) { async function trySeed(): Promise<{ credentials: Credentials; db: Knex }> { const credentials: Credentials = await inquirer.prompt( - (databaseQuestions[dbClient] as any[]).map((question: Function) => + (databaseQuestions[dbClient] as any[]).map((question: ({ client, filepath }: any) => any) => question({ client: dbClient, filepath: rootPath }) ) ); diff --git a/api/src/cli/commands/init/questions.ts b/api/src/cli/commands/init/questions.ts index dd8d4be915..26516b8667 100644 --- a/api/src/cli/commands/init/questions.ts +++ b/api/src/cli/commands/init/questions.ts @@ -1,20 +1,20 @@ import path from 'path'; -const filename = ({ filepath }: { filepath: string }) => ({ +const filename = ({ filepath }: { filepath: string }): Record => ({ type: 'input', name: 'filename', message: 'Database File Path:', default: path.join(filepath, 'data.db'), }); -const host = () => ({ +const host = (): Record => ({ type: 'input', name: 'host', message: 'Database Host:', default: '127.0.0.1', }); -const port = ({ client }: { client: string }) => ({ +const port = ({ client }: { client: string }): Record => ({ type: 'input', name: 'port', message: 'Port:', @@ -30,27 +30,27 @@ const port = ({ client }: { client: string }) => ({ }, }); -const database = () => ({ +const database = (): Record => ({ type: 'input', name: 'database', message: 'Database Name:', default: 'directus', }); -const user = () => ({ +const user = (): Record => ({ type: 'input', name: 'user', message: 'Database User:', }); -const password = () => ({ +const password = (): Record => ({ type: 'password', name: 'password', message: 'Database Password:', mask: '*', }); -const ssl = () => ({ +const ssl = (): Record => ({ type: 'confirm', name: 'ssl', message: 'Enable SSL:', diff --git a/api/src/cli/commands/roles/create.ts b/api/src/cli/commands/roles/create.ts index d2d3774e2b..5f47545ec3 100644 --- a/api/src/cli/commands/roles/create.ts +++ b/api/src/cli/commands/roles/create.ts @@ -1,6 +1,6 @@ import { getSchema } from '../../../utils/get-schema'; -export default async function rolesCreate({ name, admin }: any) { +export default async function rolesCreate({ name, admin }: any): Promise { const { default: database } = require('../../../database/index'); const { RolesService } = require('../../../services/roles'); diff --git a/api/src/cli/commands/users/create.ts b/api/src/cli/commands/users/create.ts index 16ad703662..f2f727790f 100644 --- a/api/src/cli/commands/users/create.ts +++ b/api/src/cli/commands/users/create.ts @@ -1,6 +1,6 @@ import { getSchema } from '../../../utils/get-schema'; -export default async function usersCreate({ email, password, role }: any) { +export default async function usersCreate({ email, password, role }: any): Promise { const { default: database, schemaInspector } = require('../../../database/index'); const { UsersService } = require('../../../services/users'); diff --git a/api/src/cli/commands/users/passwd.ts b/api/src/cli/commands/users/passwd.ts index d2909aa598..e28d7fc57f 100644 --- a/api/src/cli/commands/users/passwd.ts +++ b/api/src/cli/commands/users/passwd.ts @@ -1,7 +1,7 @@ import argon2 from 'argon2'; import { getSchema } from '../../../utils/get-schema'; -export default async function usersPasswd({ email, password }: any) { +export default async function usersPasswd({ email, password }: any): Promise { const { default: database } = require('../../../database/index'); const { UsersService } = require('../../../services/users'); diff --git a/api/src/cli/utils/create-db-connection.ts b/api/src/cli/utils/create-db-connection.ts index a0caf84b32..13d6273381 100644 --- a/api/src/cli/utils/create-db-connection.ts +++ b/api/src/cli/utils/create-db-connection.ts @@ -13,7 +13,7 @@ export type Credentials = { export default function createDBConnection( client: 'sqlite3' | 'mysql' | 'pg' | 'oracledb' | 'mssql', credentials: Credentials -) { +): Knex { let connection: Knex.Config['connection'] = {}; if (client === 'sqlite3') { diff --git a/api/src/cli/utils/create-env/index.ts b/api/src/cli/utils/create-env/index.ts index 1aec2d1bc1..86845ce1cd 100644 --- a/api/src/cli/utils/create-env/index.ts +++ b/api/src/cli/utils/create-env/index.ts @@ -23,7 +23,11 @@ const defaults = { }, }; -export default async function createEnv(client: keyof typeof drivers, credentials: Credentials, directory: string) { +export default async function createEnv( + client: keyof typeof drivers, + credentials: Credentials, + directory: string +): Promise { const config: Record = { ...defaults, database: { diff --git a/api/src/controllers/auth.ts b/api/src/controllers/auth.ts index 3ccf2aafff..311fd937a9 100644 --- a/api/src/controllers/auth.ts +++ b/api/src/controllers/auth.ts @@ -311,7 +311,9 @@ router.get( try { const email = getEmailFromProfile(req.params.provider, req.session.grant.response?.profile); - req.session?.destroy(() => {}); + req.session?.destroy(() => { + // Do nothing + }); authResponse = await authenticationService.authenticate({ email, diff --git a/api/src/database/index.ts b/api/src/database/index.ts index 919b89bdab..7ef406a672 100644 --- a/api/src/database/index.ts +++ b/api/src/database/index.ts @@ -55,7 +55,7 @@ database logger.trace(`[${delta.toFixed(3)}ms] ${queryInfo.sql} [${queryInfo.bindings.join(', ')}]`); }); -export async function hasDatabaseConnection() { +export async function hasDatabaseConnection(): Promise { try { if (env.DB_CLIENT === 'oracledb') { await database.raw('select 1 from DUAL'); @@ -68,7 +68,7 @@ export async function hasDatabaseConnection() { } } -export async function validateDBConnection() { +export async function validateDBConnection(): Promise { try { await hasDatabaseConnection(); } catch (error) { @@ -80,7 +80,7 @@ export async function validateDBConnection() { export const schemaInspector = SchemaInspector(database); -export async function isInstalled() { +export async function isInstalled(): Promise { // The existence of a directus_collections table alone isn't a "proper" check to see if everything // is installed correctly of course, but it's safe enough to assume that this collection only // exists when using the installer CLI. diff --git a/api/src/database/migrations/20201028A-remove-collection-foreign-keys.ts b/api/src/database/migrations/20201028A-remove-collection-foreign-keys.ts index 02a1b9b62c..e827707540 100644 --- a/api/src/database/migrations/20201028A-remove-collection-foreign-keys.ts +++ b/api/src/database/migrations/20201028A-remove-collection-foreign-keys.ts @@ -1,6 +1,6 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex.schema.alterTable('directus_fields', (table) => { table.dropForeign(['collection']); }); @@ -27,7 +27,7 @@ export async function up(knex: Knex) { }); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.schema.alterTable('directus_fields', (table) => { table.foreign('collection').references('directus_collections.collection'); }); diff --git a/api/src/database/migrations/20201029A-remove-system-relations.ts b/api/src/database/migrations/20201029A-remove-system-relations.ts index 292cb0ca83..2adafac743 100644 --- a/api/src/database/migrations/20201029A-remove-system-relations.ts +++ b/api/src/database/migrations/20201029A-remove-system-relations.ts @@ -1,14 +1,14 @@ import { Knex } from 'knex'; import { merge } from 'lodash'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex('directus_relations') .delete() .where('many_collection', 'like', 'directus_%') .andWhere('one_collection', 'like', 'directus_%'); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { const defaults = { many_collection: 'directus_users', many_field: null, diff --git a/api/src/database/migrations/20201029B-remove-system-collections.ts b/api/src/database/migrations/20201029B-remove-system-collections.ts index e6f396852e..6eebdf6f15 100644 --- a/api/src/database/migrations/20201029B-remove-system-collections.ts +++ b/api/src/database/migrations/20201029B-remove-system-collections.ts @@ -1,11 +1,11 @@ import { Knex } from 'knex'; import { merge } from 'lodash'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex('directus_collections').delete().where('collection', 'like', 'directus_%'); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { const defaults = { collection: null, hidden: false, diff --git a/api/src/database/migrations/20201029C-remove-system-fields.ts b/api/src/database/migrations/20201029C-remove-system-fields.ts index fd6b3facf4..583544b158 100644 --- a/api/src/database/migrations/20201029C-remove-system-fields.ts +++ b/api/src/database/migrations/20201029C-remove-system-fields.ts @@ -1639,12 +1639,12 @@ const systemFields = [ return merge({}, defaults, row); }); -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { const fieldKeys = uniq(systemFields.map((field: any) => field.field)); await knex('directus_fields').delete().where('collection', 'like', 'directus_%').whereIn('field', fieldKeys); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.insert(systemFields).into('directus_fields'); } diff --git a/api/src/database/migrations/20201105A-add-cascade-system-relations.ts b/api/src/database/migrations/20201105A-add-cascade-system-relations.ts index 5a7155f844..1349ee0ce9 100644 --- a/api/src/database/migrations/20201105A-add-cascade-system-relations.ts +++ b/api/src/database/migrations/20201105A-add-cascade-system-relations.ts @@ -114,7 +114,7 @@ const updates = [ * Postgres behaves erratic on those triggers, not sure if MySQL / Maria plays nice either. */ -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { for (const update of updates) { await knex.schema.alterTable(update.table, (table) => { for (const constraint of update.constraints) { @@ -125,7 +125,7 @@ export async function up(knex: Knex) { } } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { for (const update of updates) { await knex.schema.alterTable(update.table, (table) => { for (const constraint of update.constraints) { diff --git a/api/src/database/migrations/20201105B-change-webhook-url-type.ts b/api/src/database/migrations/20201105B-change-webhook-url-type.ts index ed306429ae..cc274907ae 100644 --- a/api/src/database/migrations/20201105B-change-webhook-url-type.ts +++ b/api/src/database/migrations/20201105B-change-webhook-url-type.ts @@ -1,12 +1,12 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex.schema.alterTable('directus_webhooks', (table) => { table.text('url').alter(); }); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.schema.alterTable('directus_webhooks', (table) => { table.string('url').alter(); }); diff --git a/api/src/database/migrations/20210225A-add-relations-sort-field.ts b/api/src/database/migrations/20210225A-add-relations-sort-field.ts index d056377c24..f30752db91 100644 --- a/api/src/database/migrations/20210225A-add-relations-sort-field.ts +++ b/api/src/database/migrations/20210225A-add-relations-sort-field.ts @@ -1,6 +1,6 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex.schema.alterTable('directus_relations', (table) => { table.string('sort_field'); }); @@ -26,7 +26,7 @@ export async function up(knex: Knex) { } } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.schema.alterTable('directus_relations', (table) => { table.dropColumn('sort_field'); }); diff --git a/api/src/database/migrations/20210304A-remove-locked-fields.ts b/api/src/database/migrations/20210304A-remove-locked-fields.ts index a75f9d6482..752b0375ce 100644 --- a/api/src/database/migrations/20210304A-remove-locked-fields.ts +++ b/api/src/database/migrations/20210304A-remove-locked-fields.ts @@ -1,12 +1,12 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex.schema.alterTable('directus_fields', (table) => { table.dropColumn('locked'); }); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.schema.alterTable('directus_fields', (table) => { table.boolean('locked').defaultTo(false).notNullable(); }); diff --git a/api/src/database/migrations/20210312A-webhooks-collections-text.ts b/api/src/database/migrations/20210312A-webhooks-collections-text.ts index b232ac47b7..43b31ab628 100644 --- a/api/src/database/migrations/20210312A-webhooks-collections-text.ts +++ b/api/src/database/migrations/20210312A-webhooks-collections-text.ts @@ -1,12 +1,12 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex.schema.alterTable('directus_webhooks', (table) => { table.text('collections').alter(); }); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.schema.alterTable('directus_webhooks', (table) => { table.string('collections').alter(); }); diff --git a/api/src/database/migrations/20210331A-add-refresh-interval.ts b/api/src/database/migrations/20210331A-add-refresh-interval.ts index b7d99e739e..b72321d625 100644 --- a/api/src/database/migrations/20210331A-add-refresh-interval.ts +++ b/api/src/database/migrations/20210331A-add-refresh-interval.ts @@ -1,12 +1,12 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex.schema.alterTable('directus_presets', (table) => { table.integer('refresh_interval'); }); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.schema.alterTable('directus_presets', (table) => { table.dropColumn('refresh_interval'); }); diff --git a/api/src/database/migrations/20210415A-make-filesize-nullable.ts b/api/src/database/migrations/20210415A-make-filesize-nullable.ts index 7809a87e89..e013e69831 100644 --- a/api/src/database/migrations/20210415A-make-filesize-nullable.ts +++ b/api/src/database/migrations/20210415A-make-filesize-nullable.ts @@ -1,12 +1,12 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex.schema.alterTable('directus_files', (table) => { table.integer('filesize').nullable().defaultTo(null).alter(); }); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.schema.alterTable('directus_files', (table) => { table.integer('filesize').notNullable().defaultTo(0).alter(); }); diff --git a/api/src/database/migrations/20210416A-add-collections-accountability.ts b/api/src/database/migrations/20210416A-add-collections-accountability.ts index e22bea9251..659fdb9f08 100644 --- a/api/src/database/migrations/20210416A-add-collections-accountability.ts +++ b/api/src/database/migrations/20210416A-add-collections-accountability.ts @@ -1,6 +1,6 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex.schema.alterTable('directus_collections', (table) => { table.string('accountability').defaultTo('all'); }); @@ -8,7 +8,7 @@ export async function up(knex: Knex) { await knex('directus_collections').update({ accountability: 'all' }); } -export async function down(knex: Knex) { +export async function down(knex: Knex): Promise { await knex.schema.alterTable('directus_collections', (table) => { table.dropColumn('accountability'); }); diff --git a/api/src/database/migrations/20210422A-remove-files-interface.ts b/api/src/database/migrations/20210422A-remove-files-interface.ts index 733d1a85f9..94fd73efe5 100644 --- a/api/src/database/migrations/20210422A-remove-files-interface.ts +++ b/api/src/database/migrations/20210422A-remove-files-interface.ts @@ -1,7 +1,9 @@ import { Knex } from 'knex'; -export async function up(knex: Knex) { +export async function up(knex: Knex): Promise { await knex('directus_fields').update({ interface: 'many-to-many' }).where({ interface: 'files' }); } -export async function down(knex: Knex) {} +export async function down(_knex: Knex): Promise { + // Do nothing +} diff --git a/api/src/database/migrations/run.ts b/api/src/database/migrations/run.ts index e622c671e8..2526e3a5c1 100644 --- a/api/src/database/migrations/run.ts +++ b/api/src/database/migrations/run.ts @@ -10,7 +10,7 @@ type Migration = { timestamp: Date; }; -export default async function run(database: Knex, direction: 'up' | 'down' | 'latest') { +export default async function run(database: Knex, direction: 'up' | 'down' | 'latest'): Promise { let migrationFiles = await fse.readdir(__dirname); const customMigrationsPath = path.resolve(env.EXTENSIONS_PATH, 'migrations'); diff --git a/api/src/database/seeds/run.ts b/api/src/database/seeds/run.ts index ca87491609..10e24ad940 100644 --- a/api/src/database/seeds/run.ts +++ b/api/src/database/seeds/run.ts @@ -25,7 +25,7 @@ type TableSeed = { }; }; -export default async function runSeed(database: Knex) { +export default async function runSeed(database: Knex): Promise { const exists = await database.schema.hasTable('directus_collections'); if (exists) { diff --git a/api/src/emitter.ts b/api/src/emitter.ts index 9d50e39df3..8329626ffc 100644 --- a/api/src/emitter.ts +++ b/api/src/emitter.ts @@ -15,7 +15,7 @@ const emitter = new EventEmitter2({ * @param name * @param args */ -export async function emitAsyncSafe(name: string, ...args: any[]) { +export async function emitAsyncSafe(name: string, ...args: any[]): Promise { try { return await emitter.emitAsync(name, ...args); } catch (err) { diff --git a/api/src/exceptions/database/dialects/mssql.ts b/api/src/exceptions/database/dialects/mssql.ts index 179731675a..978b37aa57 100644 --- a/api/src/exceptions/database/dialects/mssql.ts +++ b/api/src/exceptions/database/dialects/mssql.ts @@ -5,17 +5,7 @@ import { ValueTooLongException } from '../value-too-long'; import { ValueOutOfRangeException } from '../value-out-of-range'; import database from '../../../database'; - -type MSSQLError = { - message: string; - code: 'EREQUEST'; - number: number; - state: number; - class: number; - serverName: string; - procName: string; - lineNumber: number; -}; +import { MSSQLError } from './types'; enum MSSQLErrorCodes { FOREIGN_KEY_VIOLATION = 547, @@ -25,7 +15,7 @@ enum MSSQLErrorCodes { VALUE_LIMIT_VIOLATION = 2628, } -export async function extractError(error: MSSQLError) { +export async function extractError(error: MSSQLError): Promise { switch (error.number) { case MSSQLErrorCodes.UNIQUE_VIOLATION: case 2627: @@ -56,8 +46,8 @@ async function uniqueViolation(error: MSSQLError) { * information_schema when this happens */ - const betweenQuotes = /\'([^\']+)\'/; - const betweenParens = /\(([^\)]+)\)/g; + const betweenQuotes = /'([^']+)'/; + const betweenParens = /\(([^)]+)\)/g; const quoteMatches = error.message.match(betweenQuotes); const parenMatches = error.message.match(betweenParens); @@ -117,7 +107,7 @@ function numericValueOutOfRange(error: MSSQLError) { function valueLimitViolation(error: MSSQLError) { const betweenBrackets = /\[([^\]]+)\]/g; - const betweenQuotes = /\'([^\']+)\'/g; + const betweenQuotes = /'([^']+)'/g; const bracketMatches = error.message.match(betweenBrackets); const quoteMatches = error.message.match(betweenQuotes); @@ -135,7 +125,7 @@ function valueLimitViolation(error: MSSQLError) { function notNullViolation(error: MSSQLError) { const betweenBrackets = /\[([^\]]+)\]/g; - const betweenQuotes = /\'([^\']+)\'/g; + const betweenQuotes = /'([^']+)'/g; const bracketMatches = error.message.match(betweenBrackets); const quoteMatches = error.message.match(betweenQuotes); @@ -152,8 +142,8 @@ function notNullViolation(error: MSSQLError) { } function foreignKeyViolation(error: MSSQLError) { - const betweenUnderscores = /\_\_(.+)\_\_/g; - const betweenParens = /\(([^\)]+)\)/g; + const betweenUnderscores = /__(.+)__/g; + const betweenParens = /\(([^)]+)\)/g; // NOTE: // Seeing that MS SQL doesn't return the offending column name, we have to extract it from the diff --git a/api/src/exceptions/database/dialects/mysql.ts b/api/src/exceptions/database/dialects/mysql.ts index bc9f221f0d..86d919ff02 100644 --- a/api/src/exceptions/database/dialects/mysql.ts +++ b/api/src/exceptions/database/dialects/mysql.ts @@ -3,16 +3,7 @@ import { NotNullViolationException } from '../not-null-violation'; import { RecordNotUniqueException } from '../record-not-unique'; import { ValueTooLongException } from '../value-too-long'; import { ValueOutOfRangeException } from '../value-out-of-range'; - -type MySQLError = { - message: string; - code: string; - errno: number; - sqlMessage: string; - sqlState: string; - index: number; - sql: string; -}; +import { MySQLError } from './types'; enum MySQLErrorCodes { UNIQUE_VIOLATION = 'ER_DUP_ENTRY', @@ -22,7 +13,7 @@ enum MySQLErrorCodes { FOREIGN_KEY_VIOLATION = 'ER_NO_REFERENCED_ROW_2', } -export function extractError(error: MySQLError) { +export function extractError(error: MySQLError): MySQLError | Error { switch (error.code) { case MySQLErrorCodes.UNIQUE_VIOLATION: return uniqueViolation(error); @@ -39,7 +30,7 @@ export function extractError(error: MySQLError) { } function uniqueViolation(error: MySQLError) { - const betweenQuotes = /\'([^\']+)\'/g; + const betweenQuotes = /'([^']+)'/g; const matches = error.sqlMessage.match(betweenQuotes); if (!matches) return error; @@ -69,8 +60,8 @@ function uniqueViolation(error: MySQLError) { } function numericValueOutOfRange(error: MySQLError) { - const betweenTicks = /\`([^\`]+)\`/g; - const betweenQuotes = /\'([^\']+)\'/g; + const betweenTicks = /`([^`]+)`/g; + const betweenQuotes = /'([^']+)'/g; const tickMatches = error.sql.match(betweenTicks); const quoteMatches = error.sqlMessage.match(betweenQuotes); @@ -87,8 +78,8 @@ function numericValueOutOfRange(error: MySQLError) { } function valueLimitViolation(error: MySQLError) { - const betweenTicks = /\`([^\`]+)\`/g; - const betweenQuotes = /\'([^\']+)\'/g; + const betweenTicks = /`([^`]+)`/g; + const betweenQuotes = /'([^']+)'/g; const tickMatches = error.sql.match(betweenTicks); const quoteMatches = error.sqlMessage.match(betweenQuotes); @@ -105,8 +96,8 @@ function valueLimitViolation(error: MySQLError) { } function notNullViolation(error: MySQLError) { - const betweenTicks = /\`([^\`]+)\`/g; - const betweenQuotes = /\'([^\']+)\'/g; + const betweenTicks = /`([^`]+)`/g; + const betweenQuotes = /'([^']+)'/g; const tickMatches = error.sql.match(betweenTicks); const quoteMatches = error.sqlMessage.match(betweenQuotes); @@ -123,8 +114,8 @@ function notNullViolation(error: MySQLError) { } function foreignKeyViolation(error: MySQLError) { - const betweenTicks = /\`([^\`]+)\`/g; - const betweenParens = /\(([^\)]+)\)/g; + const betweenTicks = /`([^`]+)`/g; + const betweenParens = /\(([^)]+)\)/g; const tickMatches = error.sqlMessage.match(betweenTicks); const parenMatches = error.sql.match(betweenParens); diff --git a/api/src/exceptions/database/dialects/oracle.ts b/api/src/exceptions/database/dialects/oracle.ts index 4c1a1d6d94..68380de6de 100644 --- a/api/src/exceptions/database/dialects/oracle.ts +++ b/api/src/exceptions/database/dialects/oracle.ts @@ -1,3 +1,3 @@ -export function extractError(error: any) { +export function extractError(error: Error): Error { return error; } diff --git a/api/src/exceptions/database/dialects/postgres.ts b/api/src/exceptions/database/dialects/postgres.ts index d2747bceef..fca233461b 100644 --- a/api/src/exceptions/database/dialects/postgres.ts +++ b/api/src/exceptions/database/dialects/postgres.ts @@ -3,18 +3,7 @@ import { NotNullViolationException } from '../not-null-violation'; import { RecordNotUniqueException } from '../record-not-unique'; import { ValueTooLongException } from '../value-too-long'; import { ValueOutOfRangeException } from '../value-out-of-range'; - -type PostgresError = { - message: string; - length: number; - code: string; - detail: string; - schema: string; - table: string; - column?: string; - dataType?: string; - constraint?: string; -}; +import { PostgresError } from './types'; enum PostgresErrorCodes { FOREIGN_KEY_VIOLATION = '23503', @@ -24,7 +13,7 @@ enum PostgresErrorCodes { VALUE_LIMIT_VIOLATION = '22001', } -export function extractError(error: PostgresError) { +export function extractError(error: PostgresError): PostgresError | Error { switch (error.code) { case PostgresErrorCodes.UNIQUE_VIOLATION: return uniqueViolation(error); @@ -44,7 +33,7 @@ export function extractError(error: PostgresError) { function uniqueViolation(error: PostgresError) { const { table, detail } = error; - const betweenParens = /\(([^\)]+)\)/g; + const betweenParens = /\(([^)]+)\)/g; const matches = detail.match(betweenParens); if (!matches) return error; @@ -111,7 +100,7 @@ function notNullViolation(error: PostgresError) { function foreignKeyViolation(error: PostgresError) { const { table, detail } = error; - const betweenParens = /\(([^\)]+)\)/g; + const betweenParens = /\(([^)]+)\)/g; const matches = detail.match(betweenParens); if (!matches) return error; diff --git a/api/src/exceptions/database/dialects/sqlite.ts b/api/src/exceptions/database/dialects/sqlite.ts index 2ef426fcba..d9662012ff 100644 --- a/api/src/exceptions/database/dialects/sqlite.ts +++ b/api/src/exceptions/database/dialects/sqlite.ts @@ -1,18 +1,13 @@ import { InvalidForeignKeyException } from '../invalid-foreign-key'; import { RecordNotUniqueException } from '../record-not-unique'; import { NotNullViolationException } from '../not-null-violation'; - -type SQLiteError = { - message: string; - errno: number; - code: string; -}; +import { SQLiteError } from './types'; // NOTE: // - Sqlite doesn't have varchar with length support, so no ValueTooLongException // - Sqlite doesn't have a max range for numbers, so no ValueOutOfRangeException -export function extractError(error: SQLiteError) { +export function extractError(error: SQLiteError): SQLiteError | Error { if (error.message.includes('SQLITE_CONSTRAINT: NOT NULL')) { return notNullConstraint(error); } diff --git a/api/src/exceptions/database/dialects/types.ts b/api/src/exceptions/database/dialects/types.ts new file mode 100644 index 0000000000..5c05bc9543 --- /dev/null +++ b/api/src/exceptions/database/dialects/types.ts @@ -0,0 +1,40 @@ +export type MSSQLError = { + message: string; + code: 'EREQUEST'; + number: number; + state: number; + class: number; + serverName: string; + procName: string; + lineNumber: number; +}; + +export type MySQLError = { + message: string; + code: string; + errno: number; + sqlMessage: string; + sqlState: string; + index: number; + sql: string; +}; + +export type PostgresError = { + message: string; + length: number; + code: string; + detail: string; + schema: string; + table: string; + column?: string; + dataType?: string; + constraint?: string; +}; + +export type SQLiteError = { + message: string; + errno: number; + code: string; +}; + +export type SQLError = MSSQLError & MySQLError & PostgresError & SQLiteError & Error; diff --git a/api/src/exceptions/database/translate.ts b/api/src/exceptions/database/translate.ts index 8c03e423bc..a767b81b7f 100644 --- a/api/src/exceptions/database/translate.ts +++ b/api/src/exceptions/database/translate.ts @@ -4,6 +4,7 @@ import { extractError as mysql } from './dialects/mysql'; import { extractError as mssql } from './dialects/mssql'; import { extractError as sqlite } from './dialects/sqlite'; import { extractError as oracle } from './dialects/oracle'; +import { SQLError } from './dialects/types'; /** * Translates an error thrown by any of the databases into a pre-defined Exception. Currently @@ -14,17 +15,17 @@ import { extractError as oracle } from './dialects/oracle'; * - Value Out of Range * - Value Too Long */ -export async function translateDatabaseError(error: any) { +export async function translateDatabaseError(error: SQLError): Promise { switch (database.client.constructor.name) { case 'Client_MySQL': - return await mysql(error); + return mysql(error); case 'Client_PG': - return await postgres(error); + return postgres(error); case 'Client_SQLite3': - return await sqlite(error); + return sqlite(error); case 'Client_Oracledb': case 'Client_Oracle': - return await oracle(error); + return oracle(error); case 'Client_MSSQL': return await mssql(error); diff --git a/api/src/extensions.ts b/api/src/extensions.ts index 7ba1052b33..a3dc07e9f4 100644 --- a/api/src/extensions.ts +++ b/api/src/extensions.ts @@ -13,7 +13,7 @@ import * as exceptions from './exceptions'; import * as services from './services'; import database from './database'; -export async function ensureFoldersExist() { +export async function ensureFoldersExist(): Promise { const folders = ['endpoints', 'hooks', 'interfaces', 'modules', 'layouts', 'displays']; for (const folder of folders) { @@ -26,11 +26,11 @@ export async function ensureFoldersExist() { } } -export async function initializeExtensions() { +export async function initializeExtensions(): Promise { await ensureFoldersExist(); } -export async function listExtensions(type: string) { +export async function listExtensions(type: string): Promise { const extensionsPath = env.EXTENSIONS_PATH as string; const location = path.join(extensionsPath, type); @@ -46,12 +46,12 @@ export async function listExtensions(type: string) { } } -export async function registerExtensions(router: Router) { +export async function registerExtensions(router: Router): Promise { await registerExtensionHooks(); await registerExtensionEndpoints(router); } -export async function registerExtensionEndpoints(router: Router) { +export async function registerExtensionEndpoints(router: Router): Promise { let endpoints: string[] = []; try { endpoints = await listExtensions('endpoints'); @@ -61,7 +61,7 @@ export async function registerExtensionEndpoints(router: Router) { } } -export async function registerExtensionHooks() { +export async function registerExtensionHooks(): Promise { let hooks: string[] = []; try { hooks = await listExtensions('hooks'); diff --git a/api/src/middleware/use-collection.ts b/api/src/middleware/use-collection.ts index 550bf9f424..905bdda68f 100644 --- a/api/src/middleware/use-collection.ts +++ b/api/src/middleware/use-collection.ts @@ -2,9 +2,10 @@ * Set req.collection for use in other middleware. Used as an alternative on validate-collection for * system collections */ +import { RequestHandler } from 'express'; import asyncHandler from '../utils/async-handler'; -const useCollection = (collection: string) => +const useCollection = (collection: string): RequestHandler => asyncHandler(async (req, res, next) => { req.collection = collection; next(); diff --git a/api/src/rate-limiter.ts b/api/src/rate-limiter.ts index b77dcb3f66..4d276b3fb0 100644 --- a/api/src/rate-limiter.ts +++ b/api/src/rate-limiter.ts @@ -4,6 +4,7 @@ import { RateLimiterMemcache, IRateLimiterOptions, IRateLimiterStoreOptions, + RateLimiterAbstract, } from 'rate-limiter-flexible'; import { merge } from 'lodash'; @@ -13,7 +14,7 @@ import { getConfigFromEnv } from './utils/get-config-from-env'; type IRateLimiterOptionsOverrides = Partial | Partial; -export function createRateLimiter(configOverrides?: IRateLimiterOptionsOverrides) { +export function createRateLimiter(configOverrides?: IRateLimiterOptionsOverrides): RateLimiterAbstract { switch (env.RATE_LIMITER_STORE) { case 'redis': return new RateLimiterRedis(getConfig('redis', configOverrides)); diff --git a/api/src/server.ts b/api/src/server.ts index e19e796c99..be735801e6 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -10,7 +10,7 @@ import database from './database'; import createApp from './app'; import { once } from 'lodash'; -export default async function createServer() { +export default async function createServer(): Promise { const server = http.createServer(await createApp()); server.on('request', function (req: http.IncomingMessage & Request, res: http.ServerResponse) { @@ -21,22 +21,24 @@ export default async function createServer() { const elapsedNanoseconds = elapsedTime[0] * 1e9 + elapsedTime[1]; const elapsedMilliseconds = elapsedNanoseconds / 1e6; - const previousIn = (req.connection as any)._metrics?.in || 0; - const previousOut = (req.connection as any)._metrics?.out || 0; + const previousIn = (req.socket as any)._metrics?.in || 0; + const previousOut = (req.socket as any)._metrics?.out || 0; const metrics = { - in: req.connection.bytesRead - previousIn, - out: req.connection.bytesWritten - previousOut, + in: req.socket.bytesRead - previousIn, + out: req.socket.bytesWritten - previousOut, }; - (req.connection as any)._metrics = { - in: req.connection.bytesRead, - out: req.connection.bytesWritten, + (req.socket as any)._metrics = { + in: req.socket.bytesRead, + out: req.socket.bytesWritten, }; // Compatibility when supporting serving with certificates const protocol = server instanceof https.Server ? 'https' : 'http'; + // Rely on url.parse for path extraction + // Doesn't break on illegal URLs const urlInfo = url.parse(req.originalUrl || req.url); const info = { @@ -58,7 +60,7 @@ export default async function createServer() { size: metrics.out, headers: res.getHeaders(), }, - ip: req.headers['x-forwarded-for'] || req.connection?.remoteAddress || req.socket?.remoteAddress, + ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress, duration: elapsedMilliseconds.toFixed(), }; diff --git a/api/src/services/assets.ts b/api/src/services/assets.ts index f8915c1d0c..79e05d9c1f 100644 --- a/api/src/services/assets.ts +++ b/api/src/services/assets.ts @@ -5,7 +5,7 @@ import path from 'path'; import { Knex } from 'knex'; import { Accountability, AbstractServiceOptions, Transformation } from '../types'; import { AuthorizationService } from './authorization'; -import { Range } from '@directus/drive'; +import { Range, StatResponse } from '@directus/drive'; import { RangeNotSatisfiableException } from '../exceptions'; export class AssetsService { @@ -19,7 +19,11 @@ export class AssetsService { this.authorizationService = new AuthorizationService(options); } - async getAsset(id: string, transformation: Transformation, range?: Range) { + async getAsset( + id: string, + transformation: Transformation, + range?: Range + ): Promise<{ stream: NodeJS.ReadableStream; file: any; stat: StatResponse }> { const publicSettings = await this.knex .select('project_logo', 'public_background', 'public_foreground') .from('directus_settings') diff --git a/api/src/services/authentication.ts b/api/src/services/authentication.ts index 78e840f0cb..48934ab639 100644 --- a/api/src/services/authentication.ts +++ b/api/src/services/authentication.ts @@ -50,7 +50,9 @@ export class AuthenticationService { * Password is optional to allow usage of this function within the SSO flow and extensions. Make sure * to handle password existence checks elsewhere */ - async authenticate(options: AuthenticateOptions) { + async authenticate( + options: AuthenticateOptions + ): Promise<{ accessToken: any; refreshToken: any; expires: any; id?: any }> { const settingsService = new SettingsService({ knex: this.knex, schema: this.schema, @@ -196,7 +198,7 @@ export class AuthenticationService { }; } - async refresh(refreshToken: string) { + async refresh(refreshToken: string): Promise> { if (!refreshToken) { throw new InvalidCredentialsException(); } @@ -235,16 +237,16 @@ export class AuthenticationService { }; } - async logout(refreshToken: string) { + async logout(refreshToken: string): Promise { await this.knex.delete().from('directus_sessions').where({ token: refreshToken }); } - generateTFASecret() { + generateTFASecret(): string { const secret = authenticator.generateSecret(); return secret; } - async generateOTPAuthURL(pk: string, secret: string) { + async generateOTPAuthURL(pk: string, secret: string): Promise { const user = await this.knex.select('first_name', 'last_name').from('directus_users').where({ id: pk }).first(); const name = `${user.first_name} ${user.last_name}`; return authenticator.keyuri(name, 'Directus', secret); @@ -261,7 +263,7 @@ export class AuthenticationService { return authenticator.check(otp, secret); } - async verifyPassword(pk: string, password: string) { + async verifyPassword(pk: string, password: string): Promise { const userRecord = await this.knex.select('password').from('directus_users').where({ id: pk }).first(); if (!userRecord || !userRecord.password) { diff --git a/api/src/services/authorization.ts b/api/src/services/authorization.ts index 9befde38bf..972211a6b2 100644 --- a/api/src/services/authorization.ts +++ b/api/src/services/authorization.ts @@ -304,7 +304,7 @@ export class AuthorizationService { return errors; } - async checkAccess(action: PermissionsAction, collection: string, pk: PrimaryKey | PrimaryKey[]) { + async checkAccess(action: PermissionsAction, collection: string, pk: PrimaryKey | PrimaryKey[]): Promise { if (this.accountability?.admin === true) return; const itemsService = new ItemsService(collection, { diff --git a/api/src/services/collections.ts b/api/src/services/collections.ts index 2c6213bd19..de95e425d1 100644 --- a/api/src/services/collections.ts +++ b/api/src/services/collections.ts @@ -114,7 +114,10 @@ export class CollectionsService { /** * Create multiple new collections */ - async createMany(payloads: Partial & { collection: string }[], opts?: MutationOptions) { + async createMany( + payloads: Partial & { collection: string }[], + opts?: MutationOptions + ): Promise { const collections = await this.knex.transaction(async (trx) => { const service = new CollectionsService({ schema: this.schema, diff --git a/api/src/services/fields.ts b/api/src/services/fields.ts index bc93b8144c..a946a922f7 100644 --- a/api/src/services/fields.ts +++ b/api/src/services/fields.ts @@ -157,7 +157,7 @@ export class FieldsService { return result; } - async readOne(collection: string, field: string) { + async readOne(collection: string, field: string): Promise> { if (this.accountability && this.accountability.admin !== true) { if (this.hasReadAccess === false) { throw new ForbiddenException(); @@ -188,7 +188,9 @@ export class FieldsService { try { column = await this.schemaInspector.columnInfo(collection, field); column.default_value = getDefaultValue(column); - } catch {} + } finally { + // Do nothing + } const data = { collection, @@ -205,7 +207,7 @@ export class FieldsService { collection: string, field: Partial & { field: string; type: typeof types[number] }, table?: Knex.CreateTableBuilder // allows collection creation to - ) { + ): Promise { if (this.accountability && this.accountability.admin !== true) { throw new ForbiddenException('Only admins can perform this action.'); } @@ -246,7 +248,7 @@ export class FieldsService { } } - async updateField(collection: string, field: RawField) { + async updateField(collection: string, field: RawField): Promise { if (this.accountability && this.accountability.admin !== true) { throw new ForbiddenException('Only admins can perform this action'); } @@ -289,7 +291,7 @@ export class FieldsService { } /** @todo save accountability */ - async deleteField(collection: string, field: string) { + async deleteField(collection: string, field: string): Promise { if (this.accountability && this.accountability.admin !== true) { throw new ForbiddenException('Only admins can perform this action.'); } @@ -375,7 +377,7 @@ export class FieldsService { }); } - public addColumnToTable(table: Knex.CreateTableBuilder, field: RawField | Field, alter: Column | null = null) { + public addColumnToTable(table: Knex.CreateTableBuilder, field: RawField | Field, alter: Column | null = null): void { let column: Knex.ColumnBuilder; if (field.schema?.has_auto_increment) { diff --git a/api/src/services/files.ts b/api/src/services/files.ts index 24d7dc3dcc..7696f3a10c 100644 --- a/api/src/services/files.ts +++ b/api/src/services/files.ts @@ -30,7 +30,7 @@ export class FilesService extends ItemsService { stream: NodeJS.ReadableStream, data: Partial & { filename_download: string; storage: string }, primaryKey?: PrimaryKey - ) { + ): Promise { const payload = clone(data); if (primaryKey !== undefined) { @@ -140,7 +140,7 @@ export class FilesService extends ItemsService { /** * Import a single file from an external URL */ - async importOne(importURL: string, body: Partial) { + async importOne(importURL: string, body: Partial): Promise { const fileCreatePermissions = this.schema.permissions.find( (permission) => permission.collection === 'directus_files' && permission.action === 'create' ); @@ -174,7 +174,7 @@ export class FilesService extends ItemsService { ...(body || {}), }; - return await this.upload(fileResponse.data, payload); + return await this.uploadOne(fileResponse.data, payload); } /** @@ -220,7 +220,7 @@ export class FilesService extends ItemsService { stream: NodeJS.ReadableStream, data: Partial & { filename_download: string; storage: string }, primaryKey?: PrimaryKey - ) { + ): Promise { logger.warn('FilesService.upload is deprecated and will be removed before v9.0.0. Use uploadOne instead.'); return await this.uploadOne(stream, data, primaryKey); @@ -229,7 +229,7 @@ export class FilesService extends ItemsService { /** * @deprecated Use `importOne` instead */ - async import(importURL: string, body: Partial) { + async import(importURL: string, body: Partial): Promise { return await this.importOne(importURL, body); } diff --git a/api/src/services/graphql.ts b/api/src/services/graphql.ts index e6e05212e6..f9bec942ca 100644 --- a/api/src/services/graphql.ts +++ b/api/src/services/graphql.ts @@ -1,6 +1,6 @@ import { Knex } from 'knex'; import database from '../database'; -import { AbstractServiceOptions, Accountability, Query, SchemaOverview, GraphQLParams, Action } from '../types'; +import { AbstractServiceOptions, Accountability, Query, SchemaOverview, GraphQLParams, Action, Item } from '../types'; import argon2 from 'argon2'; import { GraphQLString, @@ -122,7 +122,12 @@ export class GraphQLService { /** * Execute a GraphQL structure */ - async execute({ document, variables, operationName, contextValue }: GraphQLParams) { + async execute({ + document, + variables, + operationName, + contextValue, + }: GraphQLParams): Promise { const schema = this.getSchema(); const validationErrors = validate(schema, document, specifiedRules); @@ -158,8 +163,9 @@ export class GraphQLService { */ getSchema(): GraphQLSchema; getSchema(type: 'schema'): GraphQLSchema; - getSchema(type: 'sdl'): string; - getSchema(type: 'schema' | 'sdl' = 'schema') { + getSchema(type: 'sdl'): GraphQLSchema | string; + getSchema(type: 'schema' | 'sdl' = 'schema'): GraphQLSchema | string { + // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; const schemaComposer = new SchemaComposer(); @@ -798,7 +804,7 @@ export class GraphQLService { * Generic resolver that's used for every "regular" items/system query. Converts the incoming GraphQL AST / fragments into * Directus' query structure which is then executed by the services. */ - async resolveQuery(info: GraphQLResolveInfo) { + async resolveQuery(info: GraphQLResolveInfo): Promise | null> { let collection = info.fieldName; if (this.scope === 'system') collection = `directus_${collection}`; @@ -841,7 +847,10 @@ export class GraphQLService { * Generic mutation resolver that converts the incoming GraphQL mutation AST into a Directus query and executes the * appropriate C-UD operation */ - async resolveMutation(args: Record, info: GraphQLResolveInfo) { + async resolveMutation( + args: Record, + info: GraphQLResolveInfo + ): Promise | boolean | undefined> { const action = info.fieldName.split('_')[0] as 'create' | 'update' | 'delete'; let collection = info.fieldName.substring(action.length + 1); if (this.scope === 'system') collection = `directus_${collection}`; @@ -906,7 +915,7 @@ export class GraphQLService { /** * Execute the read action on the correct service. Checks for singleton as well. */ - async read(collection: string, query: Query) { + async read(collection: string, query: Query): Promise> { const service = this.getService(collection); const result = this.schema.collections[collection].singleton @@ -919,7 +928,11 @@ export class GraphQLService { /** * Upsert and read singleton item */ - async upsertSingleton(collection: string, body: Record | Record[], query: Query) { + async upsertSingleton( + collection: string, + body: Record | Record[], + query: Query + ): Promise | boolean> { const service = this.getService(collection); try { @@ -988,7 +1001,7 @@ export class GraphQLService { rawQuery: Query, selections: readonly SelectionNode[], variableValues: GraphQLResolveInfo['variableValues'] - ) { + ): Query { const query: Query = sanitizeQuery(rawQuery, this.accountability); const parseFields = (selections: readonly SelectionNode[], parent?: string): string[] => { @@ -1052,7 +1065,7 @@ export class GraphQLService { /** * Convert Directus-Exception into a GraphQL format, so it can be returned by GraphQL properly. */ - formatError(error: BaseException | BaseException[]) { + formatError(error: BaseException | BaseException[]): GraphQLError { if (Array.isArray(error)) { return new GraphQLError(error[0].message, undefined, undefined, undefined, undefined, error[0]); } @@ -1064,7 +1077,7 @@ export class GraphQLService { * Select the correct service for the given collection. This allows the individual services to run * their custom checks (f.e. it allows UsersService to prevent updating TFA secret from outside) */ - getService(collection: string) { + getService(collection: string): RolesService { const opts = { knex: this.knex, accountability: this.accountability, @@ -1150,7 +1163,7 @@ export class GraphQLService { update: SchemaOverview; delete: SchemaOverview; } - ) { + ): SchemaComposer { const AuthTokens = schemaComposer.createObjectTC({ name: 'auth_tokens', fields: { diff --git a/api/src/services/items.ts b/api/src/services/items.ts index b792f9de84..206922d1ce 100644 --- a/api/src/services/items.ts +++ b/api/src/services/items.ts @@ -527,7 +527,7 @@ export class ItemsService implements AbstractSer /** * Upsert a single item */ - async upsertOne(payload: Partial, opts?: MutationOptions) { + async upsertOne(payload: Partial, opts?: MutationOptions): Promise { const primaryKeyField = this.schema.collections[this.collection].primary; const primaryKey: PrimaryKey | undefined = payload[primaryKeyField]; @@ -549,7 +549,7 @@ export class ItemsService implements AbstractSer /** * Upsert many items */ - async upsertMany(payloads: Partial[], opts?: MutationOptions) { + async upsertMany(payloads: Partial[], opts?: MutationOptions): Promise { const primaryKeys = await this.knex.transaction(async (trx) => { const service = new ItemsService(this.collection, { accountability: this.accountability, @@ -702,7 +702,7 @@ export class ItemsService implements AbstractSer /** * Upsert/treat collection as singleton */ - async upsertSingleton(data: Partial, opts?: MutationOptions) { + async upsertSingleton(data: Partial, opts?: MutationOptions): Promise { const primaryKeyField = this.schema.collections[this.collection].primary; const record = await this.knex.select(primaryKeyField).from(this.collection).limit(1).first(); diff --git a/api/src/services/meta.ts b/api/src/services/meta.ts index f14c1f9785..b098673946 100644 --- a/api/src/services/meta.ts +++ b/api/src/services/meta.ts @@ -17,7 +17,7 @@ export class MetaService { this.schema = options.schema; } - async getMetaForQuery(collection: string, query: Query) { + async getMetaForQuery(collection: string, query: Query): Promise | undefined> { if (!query || !query.meta) return; const results = await Promise.all( @@ -35,7 +35,7 @@ export class MetaService { }, {}); } - async totalCount(collection: string) { + async totalCount(collection: string): Promise { const dbQuery = this.knex(collection).count('*', { as: 'count' }).first(); if (this.accountability?.admin !== true) { @@ -55,7 +55,7 @@ export class MetaService { return Number(result?.count ?? 0); } - async filterCount(collection: string, query: Query) { + async filterCount(collection: string, query: Query): Promise { const dbQuery = this.knex(collection).count('*', { as: 'count' }); let filter = query.filter || {}; diff --git a/api/src/services/payload.ts b/api/src/services/payload.ts index 4d21b33e2f..aef275b77a 100644 --- a/api/src/services/payload.ts +++ b/api/src/services/payload.ts @@ -191,7 +191,7 @@ export class PayloadService { payload: Partial, action: Action, accountability: Accountability | null - ) { + ): Promise { if (!field.special) return payload[field.field]; const fieldSpecials = field.special ? toArray(field.special) : []; @@ -215,7 +215,10 @@ export class PayloadService { * Knex returns `datetime` and `date` columns as Date.. This is wrong for date / datetime, as those * shouldn't return with time / timezone info respectively */ - async processDates(payloads: Partial>[], action: Action) { + async processDates( + payloads: Partial>[], + action: Action + ): Promise>[]> { const fieldsInCollection = Object.entries(this.schema.collections[this.collection].fields); const dateColumns = fieldsInCollection.filter(([name, field]) => diff --git a/api/src/services/permissions.ts b/api/src/services/permissions.ts index 3fde5c8914..3d71db74e5 100644 --- a/api/src/services/permissions.ts +++ b/api/src/services/permissions.ts @@ -10,7 +10,7 @@ export class PermissionsService extends ItemsService { super('directus_permissions', options); } - getAllowedFields(action: PermissionsAction, collection?: string) { + getAllowedFields(action: PermissionsAction, collection?: string): Record { const results = this.schema.permissions.filter((permission) => { let matchesCollection = true; diff --git a/api/src/services/revisions.ts b/api/src/services/revisions.ts index 8d79e8bb01..2f89dfe692 100644 --- a/api/src/services/revisions.ts +++ b/api/src/services/revisions.ts @@ -7,7 +7,7 @@ export class RevisionsService extends ItemsService { super('directus_revisions', options); } - async revert(pk: PrimaryKey) { + async revert(pk: PrimaryKey): Promise { const revision = await super.readOne(pk); if (!revision) throw new ForbiddenException(); diff --git a/api/src/services/server.ts b/api/src/services/server.ts index 142b739851..01a15f70cf 100644 --- a/api/src/services/server.ts +++ b/api/src/services/server.ts @@ -30,7 +30,7 @@ export class ServerService { this.settingsService = new SettingsService({ knex: this.knex, schema: this.schema }); } - async serverInfo() { + async serverInfo(): Promise> { const info: Record = {}; const projectInfo = await this.settingsService.readSingleton({ @@ -70,7 +70,7 @@ export class ServerService { return info; } - async health() { + async health(): Promise> { const checkID = nanoid(5); // Based on https://tools.ietf.org/id/draft-inadarei-api-health-check-05.html#name-componenttype diff --git a/api/src/services/users.ts b/api/src/services/users.ts index decc8c05fa..9df94f1fb1 100644 --- a/api/src/services/users.ts +++ b/api/src/services/users.ts @@ -217,7 +217,7 @@ export class UsersService extends ItemsService { return keys; } - async inviteUser(email: string | string[], role: string, url: string | null) { + async inviteUser(email: string | string[], role: string, url: string | null): Promise { const emails = toArray(email); const urlWhitelist = toArray(env.USER_INVITE_URL_ALLOW_LIST); @@ -263,7 +263,7 @@ export class UsersService extends ItemsService { }); } - async acceptInvite(token: string, password: string) { + async acceptInvite(token: string, password: string): Promise { const { email, scope } = jwt.verify(token, env.SECRET as string) as { email: string; scope: string; @@ -286,7 +286,7 @@ export class UsersService extends ItemsService { } } - async requestPasswordReset(email: string, url: string | null) { + async requestPasswordReset(email: string, url: string | null): Promise { const user = await this.knex.select('id').from('directus_users').where({ email }).first(); if (!user) throw new ForbiddenException(); @@ -321,7 +321,7 @@ export class UsersService extends ItemsService { }); } - async resetPassword(token: string, password: string) { + async resetPassword(token: string, password: string): Promise { const { email, scope } = jwt.verify(token, env.SECRET as string) as { email: string; scope: string; @@ -344,7 +344,7 @@ export class UsersService extends ItemsService { } } - async enableTFA(pk: string) { + async enableTFA(pk: string): Promise> { const user = await this.knex.select('tfa_secret').from('directus_users').where({ id: pk }).first(); if (user?.tfa_secret !== null) { @@ -366,7 +366,7 @@ export class UsersService extends ItemsService { }; } - async disableTFA(pk: string) { + async disableTFA(pk: string): Promise { await this.knex('directus_users').update({ tfa_secret: null }).where({ id: pk }); } diff --git a/api/src/services/utils.ts b/api/src/services/utils.ts index 861b096f1b..94897813d6 100644 --- a/api/src/services/utils.ts +++ b/api/src/services/utils.ts @@ -15,7 +15,7 @@ export class UtilsService { this.schema = options.schema; } - async sort(collection: string, { item, to }: { item: PrimaryKey; to: PrimaryKey }) { + async sort(collection: string, { item, to }: { item: PrimaryKey; to: PrimaryKey }): Promise { const sortFieldResponse = (await this.knex.select('sort_field').from('directus_collections').where({ collection }).first()) || systemCollectionRows; diff --git a/api/src/start.ts b/api/src/start.ts index 820b32317e..392567b8ca 100644 --- a/api/src/start.ts +++ b/api/src/start.ts @@ -7,7 +7,7 @@ if (require.main === module) { start(); } -export default async function start() { +export default async function start(): Promise { const createServer = require('./server').default; const server = await createServer(); diff --git a/api/src/types/deep-partial.d.ts b/api/src/types/deep-partial.d.ts index 651a56c220..bc66618da4 100644 --- a/api/src/types/deep-partial.d.ts +++ b/api/src/types/deep-partial.d.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/ban-types */ + type Primitive = string | number | boolean | bigint | symbol | undefined | null; type Builtin = Primitive | Function | Date | Error | RegExp; type IsTuple = T extends [infer A] diff --git a/api/src/types/express-session.d.ts b/api/src/types/express-session.d.ts index 64d8a01576..6a5866cbaa 100644 --- a/api/src/types/express-session.d.ts +++ b/api/src/types/express-session.d.ts @@ -1,4 +1,6 @@ -import * as expressSession from 'express-session'; +import { SessionData } from 'express-session'; + +export = SessionData; declare module 'express-session' { interface SessionData { diff --git a/api/src/utils/apply-query.ts b/api/src/utils/apply-query.ts index 719154744a..20bfc80fab 100644 --- a/api/src/utils/apply-query.ts +++ b/api/src/utils/apply-query.ts @@ -13,7 +13,7 @@ export default function applyQuery( query: Query, schema: SchemaOverview, subQuery: boolean = false -) { +): void { if (query.sort) { dbQuery.orderBy( query.sort.map((sort) => ({ @@ -50,7 +50,7 @@ export function applyFilter( rootFilter: Filter, collection: string, subQuery: boolean = false -) { +): void { const relations: Relation[] = [...schema.relations, ...systemRelationRows]; const aliasMap: Record = {}; @@ -332,7 +332,7 @@ export async function applySearch( dbQuery: Knex.QueryBuilder, searchQuery: string, collection: string -) { +): Promise { const fields = Object.entries(schema.collections[collection].fields); dbQuery.andWhere(function () { diff --git a/api/src/utils/deep-map.ts b/api/src/utils/deep-map.ts index de03b0d06c..854c2274c3 100644 --- a/api/src/utils/deep-map.ts +++ b/api/src/utils/deep-map.ts @@ -1,6 +1,7 @@ export function deepMap( object: Record, iterator: (value: any, key: string | number) => any, + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types context?: any ): any { if (Array.isArray(object)) { diff --git a/api/src/utils/filter-items.ts b/api/src/utils/filter-items.ts index 3a8854ffbc..17538f16cb 100644 --- a/api/src/utils/filter-items.ts +++ b/api/src/utils/filter-items.ts @@ -6,7 +6,7 @@ import generateJoi from './generate-joi'; existing array of items has to be filtered using the same filter syntax as used in the ast-to-sql flow */ -export function filterItems(items: Record[], filter: Query['filter']) { +export function filterItems(items: Record[], filter: Query['filter']): Record[] { if (!filter) return items; return items.filter((item) => { diff --git a/api/src/utils/get-cache-key.ts b/api/src/utils/get-cache-key.ts index e6f0c7e4d5..7f0990ed43 100644 --- a/api/src/utils/get-cache-key.ts +++ b/api/src/utils/get-cache-key.ts @@ -1,7 +1,7 @@ import { Request } from 'express'; import url from 'url'; -export function getCacheKey(req: Request) { +export function getCacheKey(req: Request): string { const path = url.parse(req.originalUrl).pathname; let key: string; diff --git a/api/src/utils/get-config-from-env.ts b/api/src/utils/get-config-from-env.ts index a3661078fd..671a4c52d4 100644 --- a/api/src/utils/get-config-from-env.ts +++ b/api/src/utils/get-config-from-env.ts @@ -2,7 +2,7 @@ import camelcase from 'camelcase'; import env from '../env'; import { set } from 'lodash'; -export function getConfigFromEnv(prefix: string, omitPrefix?: string | string[]) { +export function getConfigFromEnv(prefix: string, omitPrefix?: string | string[]): any { const config: any = {}; for (const [key, value] of Object.entries(env)) { diff --git a/api/src/utils/get-default-value.ts b/api/src/utils/get-default-value.ts index bfc70573bd..4bf11a14d9 100644 --- a/api/src/utils/get-default-value.ts +++ b/api/src/utils/get-default-value.ts @@ -2,7 +2,9 @@ import getLocalType from './get-local-type'; import { Column } from 'knex-schema-inspector/dist/types/column'; import { SchemaOverview } from '@directus/schema/dist/types/overview'; -export default function getDefaultValue(column: SchemaOverview[string]['columns'][string] | Column) { +export default function getDefaultValue( + column: SchemaOverview[string]['columns'][string] | Column +): string | boolean | null { const type = getLocalType(column); let defaultValue = column.default_value ?? null; diff --git a/api/src/utils/get-email-from-profile.ts b/api/src/utils/get-email-from-profile.ts index aa0194e31b..42da9e1fd0 100644 --- a/api/src/utils/get-email-from-profile.ts +++ b/api/src/utils/get-email-from-profile.ts @@ -15,7 +15,7 @@ const profileMap: Record = {}; * * This is used in the SSO flow to extract the users */ -export default function getEmailFromProfile(provider: string, profile: Record) { +export default function getEmailFromProfile(provider: string, profile: Record): string { const path = profileMap[provider] || env[`OAUTH_${provider.toUpperCase()}_PROFILE_EMAIL`] || 'email'; const email = get(profile, path); diff --git a/api/src/utils/get-graphql-type.ts b/api/src/utils/get-graphql-type.ts index c3c474d781..fb36a63364 100644 --- a/api/src/utils/get-graphql-type.ts +++ b/api/src/utils/get-graphql-type.ts @@ -1,9 +1,9 @@ -import { GraphQLBoolean, GraphQLFloat, GraphQLInt, GraphQLString } from 'graphql'; +import { GraphQLBoolean, GraphQLFloat, GraphQLInt, GraphQLScalarType, GraphQLString } from 'graphql'; import { GraphQLJSON } from 'graphql-compose'; import { GraphQLDate } from '../services/graphql'; import { types } from '../types'; -export function getGraphQLType(localType: typeof types[number] | 'alias' | 'unknown') { +export function getGraphQLType(localType: typeof types[number] | 'alias' | 'unknown'): GraphQLScalarType { switch (localType) { case 'boolean': return GraphQLBoolean; diff --git a/api/src/utils/is-jwt.ts b/api/src/utils/is-jwt.ts index c9de7a702e..819d7405be 100644 --- a/api/src/utils/is-jwt.ts +++ b/api/src/utils/is-jwt.ts @@ -4,7 +4,7 @@ import logger from '../logger'; /** * Check if a given string conforms to the structure of a JWT. */ -export default function isJWT(string: string) { +export default function isJWT(string: string): boolean { const parts = string.split('.'); // JWTs have the structure header.payload.signature diff --git a/api/src/utils/list-folders.ts b/api/src/utils/list-folders.ts index 192f0615ab..6b06d241b6 100644 --- a/api/src/utils/list-folders.ts +++ b/api/src/utils/list-folders.ts @@ -5,7 +5,7 @@ import { promisify } from 'util'; const readdir = promisify(fs.readdir); const stat = promisify(fs.stat); -export default async function listFolders(location: string) { +export default async function listFolders(location: string): Promise { const fullPath = path.resolve(location); const files = await readdir(fullPath); diff --git a/api/src/utils/parse-filter.ts b/api/src/utils/parse-filter.ts index fddf3dde06..8ca2580c12 100644 --- a/api/src/utils/parse-filter.ts +++ b/api/src/utils/parse-filter.ts @@ -2,7 +2,7 @@ import { Filter, Accountability } from '../types'; import { deepMap } from './deep-map'; import { toArray } from '../utils/to-array'; -export function parseFilter(filter: Filter, accountability: Accountability | null) { +export function parseFilter(filter: Filter, accountability: Accountability | null): any { return deepMap(filter, (val, key) => { if (val === 'true') return true; if (val === 'false') return false; diff --git a/api/src/utils/parse-iptc.ts b/api/src/utils/parse-iptc.ts index dd4ade64ca..21dd8fc187 100644 --- a/api/src/utils/parse-iptc.ts +++ b/api/src/utils/parse-iptc.ts @@ -13,7 +13,7 @@ const IPTC_ENTRY_TYPES = new Map([ const IPTC_ENTRY_MARKER = Buffer.from([0x1c, 0x02]); -export default function parseIPTC(buffer: Buffer) { +export default function parseIPTC(buffer: Buffer): Record { if (!Buffer.isBuffer(buffer)) return {}; let iptc: Record = {}; diff --git a/api/src/utils/reduce-schema.ts b/api/src/utils/reduce-schema.ts index e3778aef5f..25ba34a892 100644 --- a/api/src/utils/reduce-schema.ts +++ b/api/src/utils/reduce-schema.ts @@ -11,7 +11,7 @@ import { uniq } from 'lodash'; export function reduceSchema( schema: SchemaOverview, actions: PermissionsAction[] = ['create', 'read', 'update', 'delete'] -) { +): SchemaOverview { const reduced: SchemaOverview = { collections: {}, relations: [], diff --git a/api/src/utils/require-yaml.ts b/api/src/utils/require-yaml.ts index f7c4c802ed..c3b69fb534 100644 --- a/api/src/utils/require-yaml.ts +++ b/api/src/utils/require-yaml.ts @@ -1,7 +1,7 @@ import fse from 'fs-extra'; import yaml from 'js-yaml'; -export function requireYAML(filepath: string) { +export function requireYAML(filepath: string): Record { const yamlRaw = fse.readFileSync(filepath, 'utf8'); return yaml.load(yamlRaw) as Record; diff --git a/api/src/utils/sanitize-query.ts b/api/src/utils/sanitize-query.ts index 4b69db685e..c248a22bc8 100644 --- a/api/src/utils/sanitize-query.ts +++ b/api/src/utils/sanitize-query.ts @@ -3,7 +3,7 @@ import logger from '../logger'; import { parseFilter } from '../utils/parse-filter'; import { flatten, set, merge, get } from 'lodash'; -export function sanitizeQuery(rawQuery: Record, accountability?: Accountability | null) { +export function sanitizeQuery(rawQuery: Record, accountability?: Accountability | null): Query { const query: Query = {}; if (rawQuery.limit !== undefined) { diff --git a/api/src/utils/track.ts b/api/src/utils/track.ts index aca5443a93..1da20d2cc5 100644 --- a/api/src/utils/track.ts +++ b/api/src/utils/track.ts @@ -9,7 +9,7 @@ import env from '../env'; // @ts-ignore import { version } from '../../package.json'; -export async function track(event: string) { +export async function track(event: string): Promise { if (env.TELEMETRY !== false) { const info = await getEnvInfo(event); diff --git a/api/src/utils/validate-env.ts b/api/src/utils/validate-env.ts index d1eaabfd44..bf1a8d0a4f 100644 --- a/api/src/utils/validate-env.ts +++ b/api/src/utils/validate-env.ts @@ -1,7 +1,7 @@ import logger from '../logger'; import env from '../env'; -export function validateEnv(requiredKeys: string[]) { +export function validateEnv(requiredKeys: string[]): void { if (env.DB_CLIENT && env.DB_CLIENT === 'sqlite3') { requiredKeys.push('DB_FILENAME'); } else if (env.DB_CLIENT && env.DB_CLIENT === 'oracledb') { diff --git a/api/src/utils/validate-query.ts b/api/src/utils/validate-query.ts index 72085315fc..9e4e6348db 100644 --- a/api/src/utils/validate-query.ts +++ b/api/src/utils/validate-query.ts @@ -22,7 +22,7 @@ const querySchema = Joi.object({ deep: Joi.object(), }).id('query'); -export function validateQuery(query: Query) { +export function validateQuery(query: Query): Query { const { error } = querySchema.validate(query); if (query.filter && Object.keys(query.filter).length > 0) { diff --git a/api/src/webhooks.ts b/api/src/webhooks.ts index c1e79e7eb4..76c82db5bf 100644 --- a/api/src/webhooks.ts +++ b/api/src/webhooks.ts @@ -7,7 +7,7 @@ import logger from './logger'; let registered: { event: string; handler: ListenerFn }[] = []; -export async function register() { +export async function register(): Promise { unregister(); const webhooks = await database.select('*').from('directus_webhooks').where({ status: 'active' }); @@ -29,7 +29,7 @@ export async function register() { } } -export function unregister() { +export function unregister(): void { for (const { event, handler } of registered) { emitter.off(event, handler); } diff --git a/app/src/api.ts b/app/src/api.ts index 0aac31852e..4a3aef85e5 100644 --- a/app/src/api.ts +++ b/app/src/api.ts @@ -24,7 +24,7 @@ export interface RequestError extends AxiosError { response: Response; } -export const onRequest = (config: AxiosRequestConfig) => { +export const onRequest = (config: AxiosRequestConfig): RequestConfig => { const requestsStore = useRequestsStore(); const id = requestsStore.startRequest(); @@ -36,14 +36,14 @@ export const onRequest = (config: AxiosRequestConfig) => { return requestConfig; }; -export const onResponse = (response: AxiosResponse | Response) => { +export const onResponse = (response: AxiosResponse | Response): AxiosResponse | Response => { const requestsStore = useRequestsStore(); const id = (response.config as RequestConfig).id; requestsStore.endRequest(id); return response; }; -export const onError = async (error: RequestError) => { +export const onError = async (error: RequestError): Promise => { const requestsStore = useRequestsStore(); const id = (error.response.config as RequestConfig).id; requestsStore.endRequest(id); @@ -64,7 +64,7 @@ export const onError = async (error: RequestError) => { error.request.responseURL.includes('login') === false && error.request.responseURL.includes('tfa') === false ) { - let newToken: string; + let newToken: string | undefined; try { newToken = await refresh(); @@ -95,7 +95,7 @@ function getToken() { return api.defaults.headers?.['Authorization']?.split(' ')[1] || null; } -export function addTokenToURL(url: string, token?: string) { +export function addTokenToURL(url: string, token?: string): string { token = token || getToken(); if (!token) return url; diff --git a/app/src/auth.ts b/app/src/auth.ts index 5823cd6161..c025e8493d 100644 --- a/app/src/auth.ts +++ b/app/src/auth.ts @@ -9,7 +9,7 @@ export type LoginCredentials = { password: string; }; -export async function login(credentials: LoginCredentials) { +export async function login(credentials: LoginCredentials): Promise { const appStore = useAppStore(); const response = await api.post(`/auth/login`, { @@ -38,7 +38,7 @@ export async function login(credentials: LoginCredentials) { let refreshTimeout: any; -export async function refresh({ navigate }: LogoutOptions = { navigate: true }) { +export async function refresh({ navigate }: LogoutOptions = { navigate: true }): Promise { const appStore = useAppStore(); try { @@ -79,7 +79,7 @@ export type LogoutOptions = { /** * Everything that should happen when someone logs out, or is logged out through an external factor */ -export async function logout(optionsRaw: LogoutOptions = {}) { +export async function logout(optionsRaw: LogoutOptions = {}): Promise { const appStore = useAppStore(); const defaultOptions: Required = { diff --git a/app/src/components/transition/expand/transition-expand-methods.ts b/app/src/components/transition/expand/transition-expand-methods.ts index e0e5386e97..97048af1ba 100644 --- a/app/src/components/transition/expand/transition-expand-methods.ts +++ b/app/src/components/transition/expand/transition-expand-methods.ts @@ -11,7 +11,7 @@ interface HTMLExpandElement extends HTMLElement { }; } -export default function (expandedParentClass = '', xAxis = false) { +export default function (expandedParentClass = '', xAxis = false): Record { const sizeProperty = xAxis ? 'width' : ('height' as 'width' | 'height'); const offsetProperty = `offset${capitalizeFirst(sizeProperty)}` as 'offsetHeight' | 'offsetWidth'; diff --git a/app/src/components/v-form/form-field-interface.vue b/app/src/components/v-form/form-field-interface.vue index e8687899f0..c8609eec29 100644 --- a/app/src/components/v-form/form-field-interface.vue +++ b/app/src/components/v-form/form-field-interface.vue @@ -38,6 +38,7 @@ import { defineComponent, PropType, computed, ref } from '@vue/composition-api'; import { Field } from '@/types'; import { getInterfaces } from '@/interfaces'; import { getDefaultInterfaceForType } from '@/utils/get-default-interface-for-type'; +import { InterfaceConfig } from '@/interfaces/types'; export default defineComponent({ props: { @@ -74,7 +75,9 @@ export default defineComponent({ const { interfaces } = getInterfaces(); const interfaceExists = computed(() => { - return !!interfaces.value.find((inter) => inter.id === props.field?.meta?.interface || 'text-input'); + return !!interfaces.value.find( + (inter: InterfaceConfig) => inter.id === props.field?.meta?.interface || 'text-input' + ); }); return { interfaceExists, getDefaultInterfaceForType }; diff --git a/app/src/components/v-form/v-form.vue b/app/src/components/v-form/v-form.vue index 8e3e44d3b1..0eee1c5520 100644 --- a/app/src/components/v-form/v-form.vue +++ b/app/src/components/v-form/v-form.vue @@ -39,7 +39,7 @@ diff --git a/app/src/shims.d.ts b/app/src/shims.d.ts index 19e9e02185..c5fb050753 100644 --- a/app/src/shims.d.ts +++ b/app/src/shims.d.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/ban-types */ + declare module '*.vue' { import Vue from 'vue'; export default Vue; diff --git a/app/src/utils/abbreviate-number.ts b/app/src/utils/abbreviate-number.ts index 39b2dca2dc..c61c951431 100644 --- a/app/src/utils/abbreviate-number.ts +++ b/app/src/utils/abbreviate-number.ts @@ -1,4 +1,4 @@ -export function abbreviateNumber(value: number) { +export function abbreviateNumber(value: number): number | string { if (value >= 1000) { const suffixes = ['', 'K', 'M', 'B', 'T']; const suffixNum = Math.floor(('' + value).length / 3); diff --git a/app/src/utils/add-query-to-path.ts b/app/src/utils/add-query-to-path.ts index c1c054cd45..a793299aa6 100644 --- a/app/src/utils/add-query-to-path.ts +++ b/app/src/utils/add-query-to-path.ts @@ -1,4 +1,4 @@ -export function addQueryToPath(path: string, query: Record) { +export function addQueryToPath(path: string, query: Record): string { const queryParams = []; for (const [key, value] of Object.entries(query)) { diff --git a/app/src/utils/adjust-fields-for-displays/adjust-fields-for-displays.ts b/app/src/utils/adjust-fields-for-displays/adjust-fields-for-displays.ts index bd4ed88a3a..62b94f463a 100644 --- a/app/src/utils/adjust-fields-for-displays/adjust-fields-for-displays.ts +++ b/app/src/utils/adjust-fields-for-displays/adjust-fields-for-displays.ts @@ -1,8 +1,9 @@ import { useFieldsStore } from '@/stores/'; import { Field } from '@/types/'; import { getDisplays } from '@/displays'; +import { DisplayConfig } from '@/displays/types'; -export default function adjustFieldsForDisplays(fields: readonly string[], parentCollection: string) { +export default function adjustFieldsForDisplays(fields: readonly string[], parentCollection: string): string[] { const fieldsStore = useFieldsStore(); const { displays } = getDisplays(); @@ -13,7 +14,7 @@ export default function adjustFieldsForDisplays(fields: readonly string[], paren if (!field) return fieldKey; if (field.meta?.display === null) return fieldKey; - const display = displays.value.find((d) => d.id === field.meta?.display); + const display = displays.value.find((d: DisplayConfig) => d.id === field.meta?.display); if (!display) return fieldKey; if (!display?.fields) return fieldKey; diff --git a/app/src/utils/arrays-are-equal/arrays-are-equal.ts b/app/src/utils/arrays-are-equal/arrays-are-equal.ts index 85ae0d0dfd..d2643319d0 100644 --- a/app/src/utils/arrays-are-equal/arrays-are-equal.ts +++ b/app/src/utils/arrays-are-equal/arrays-are-equal.ts @@ -1,4 +1,4 @@ -export default function arraysAreEqual(a1: readonly (string | number)[], a2: readonly (string | number)[]) { +export default function arraysAreEqual(a1: readonly (string | number)[], a2: readonly (string | number)[]): boolean { const superSet: { [key: string]: number; } = {}; diff --git a/app/src/utils/deep-map.ts b/app/src/utils/deep-map.ts index 8cfccb3904..5cf469edaa 100644 --- a/app/src/utils/deep-map.ts +++ b/app/src/utils/deep-map.ts @@ -1,6 +1,7 @@ import { transform, isPlainObject } from 'lodash'; -export function deepMap(obj: Record, iterator: Function, context?: Function) { +// eslint-disable-next-line @typescript-eslint/ban-types +export function deepMap(obj: Record, iterator: Function, context?: Function): any { return transform(obj, function (result: any, val, key) { result[key] = isPlainObject(val) ? deepMap(val, iterator, context) : iterator.call(context, val, key, obj); }); diff --git a/app/src/utils/filters-to-query/filters-to-query.ts b/app/src/utils/filters-to-query/filters-to-query.ts index a8f1f15de6..677335927d 100644 --- a/app/src/utils/filters-to-query/filters-to-query.ts +++ b/app/src/utils/filters-to-query/filters-to-query.ts @@ -1,7 +1,7 @@ import { Filter } from '@/types/'; import { clone } from 'lodash'; -export default function filtersToQuery(filters: readonly Filter[]) { +export default function filtersToQuery(filters: readonly Filter[]): any { const filterList: Record[] = []; for (const filter of filters) { diff --git a/app/src/utils/format-filesize/format-filesize.ts b/app/src/utils/format-filesize/format-filesize.ts index c91447bc75..a1989ccffb 100644 --- a/app/src/utils/format-filesize/format-filesize.ts +++ b/app/src/utils/format-filesize/format-filesize.ts @@ -1,4 +1,4 @@ -export default function formatFilesize(bytes = 0, decimal = true) { +export default function formatFilesize(bytes = 0, decimal = true): string { const threshold = decimal ? 1000 : 1024; if (Boolean(bytes) === false) { diff --git a/app/src/utils/generate-joi/index.ts b/app/src/utils/generate-joi/index.ts index 20e810db13..663c8d828c 100644 --- a/app/src/utils/generate-joi/index.ts +++ b/app/src/utils/generate-joi/index.ts @@ -64,7 +64,7 @@ const defaults: JoiOptions = { allowUnknown: true, }; -export default function generateJoi(filter: Record | null, options?: JoiOptions) { +export default function generateJoi(filter: Record | null, options?: JoiOptions): any { filter = filter || {}; options = { diff --git a/app/src/utils/get-date-fns-locale/get-date-fns-locale.ts b/app/src/utils/get-date-fns-locale/get-date-fns-locale.ts index 1aa3b16bda..36b79a2f7e 100644 --- a/app/src/utils/get-date-fns-locale/get-date-fns-locale.ts +++ b/app/src/utils/get-date-fns-locale/get-date-fns-locale.ts @@ -1,6 +1,6 @@ import { i18n } from '@/lang'; -export async function getDateFNSLocale() { +export async function getDateFNSLocale(): Promise { const lang = i18n.locale; const localesToTry = [lang, lang.split('-')[0], 'en-US']; diff --git a/app/src/utils/get-default-interface-for-type/get-default-interface-for-type.ts b/app/src/utils/get-default-interface-for-type/get-default-interface-for-type.ts index ae9970f4cb..b85df428c1 100644 --- a/app/src/utils/get-default-interface-for-type/get-default-interface-for-type.ts +++ b/app/src/utils/get-default-interface-for-type/get-default-interface-for-type.ts @@ -25,6 +25,6 @@ const defaultInterfaceMap: Record = { * @todo default to correct interfaces for uuid / enum */ -export default function getDefaultInterfaceForType(type: typeof types[number]) { +export default function getDefaultInterfaceForType(type: typeof types[number]): string { return defaultInterfaceMap[type] || 'text-input'; } diff --git a/app/src/utils/get-endpoint.ts b/app/src/utils/get-endpoint.ts index f61262d7e0..ee747c5627 100644 --- a/app/src/utils/get-endpoint.ts +++ b/app/src/utils/get-endpoint.ts @@ -1,4 +1,4 @@ -export function getEndpoint(collection: string) { +export function getEndpoint(collection: string): string { if (collection.startsWith('directus_')) { return `/${collection.substring(9)}`; } diff --git a/app/src/utils/get-fields-from-template.ts b/app/src/utils/get-fields-from-template.ts index a154e0de94..20d0df2952 100644 --- a/app/src/utils/get-fields-from-template.ts +++ b/app/src/utils/get-fields-from-template.ts @@ -1,4 +1,4 @@ -export function getFieldsFromTemplate(template: string | null) { +export function getFieldsFromTemplate(template: string | null): string[] { if (template === null) return []; const regex = /{{(.*?)}}/g; diff --git a/app/src/utils/get-js-type.ts b/app/src/utils/get-js-type.ts index 34d3901e4f..ba8f162b85 100644 --- a/app/src/utils/get-js-type.ts +++ b/app/src/utils/get-js-type.ts @@ -1,6 +1,6 @@ import { types } from '@/types'; -export function getJSType(type: typeof types[number]) { +export function getJSType(type: typeof types[number]): string { if (['bigInteger', 'integer', 'float', 'decimal'].includes(type)) return 'number'; if (['string', 'text', 'uuid', 'hash'].includes(type)) return 'string'; if (['boolean'].includes(type)) return 'boolean'; diff --git a/app/src/utils/get-related-collection/get-related-collection.ts b/app/src/utils/get-related-collection/get-related-collection.ts index 007c9d6a24..2f23d51c44 100644 --- a/app/src/utils/get-related-collection/get-related-collection.ts +++ b/app/src/utils/get-related-collection/get-related-collection.ts @@ -1,6 +1,7 @@ import { useFieldsStore, useRelationsStore } from '@/stores/'; +import { Collection } from '@/types'; -export default function getRelatedCollection(collection: string, field: string) { +export default function getRelatedCollection(collection: string, field: string): Collection { const relationsStore = useRelationsStore(); const fieldsStore = useFieldsStore(); diff --git a/app/src/utils/get-root-path.ts b/app/src/utils/get-root-path.ts index 32be423c91..4f16792083 100644 --- a/app/src/utils/get-root-path.ts +++ b/app/src/utils/get-root-path.ts @@ -6,7 +6,7 @@ export function getRootPath(): string { return rootPath; } -export function getPublicURL() { +export function getPublicURL(): string { const path = window.location.href; const parts = path.split('/'); const adminIndex = parts.indexOf('admin'); diff --git a/app/src/utils/hide-drag-image/hide-drag-image.ts b/app/src/utils/hide-drag-image/hide-drag-image.ts index 97c2788360..86650db1c7 100644 --- a/app/src/utils/hide-drag-image/hide-drag-image.ts +++ b/app/src/utils/hide-drag-image/hide-drag-image.ts @@ -1,4 +1,4 @@ -export default function hideDragImage(dataTransfer: DataTransfer) { +export default function hideDragImage(dataTransfer: DataTransfer): void { const emptyImg = new Image(); dataTransfer.setDragImage(emptyImg, 0, 0); } diff --git a/app/src/utils/hljs-graphql.ts b/app/src/utils/hljs-graphql.ts index 3da2a04239..1433a7d86f 100644 --- a/app/src/utils/hljs-graphql.ts +++ b/app/src/utils/hljs-graphql.ts @@ -1,6 +1,6 @@ import { HASH_COMMENT_MODE, QUOTE_STRING_MODE, NUMBER_MODE } from 'highlight.js'; -export default () => ({ +export default (): LanguageDetail & ModeDetails => ({ aliases: ['gql'], keywords: { keyword: diff --git a/app/src/utils/is-allowed.ts b/app/src/utils/is-allowed.ts index 513bca3523..fe754aa564 100644 --- a/app/src/utils/is-allowed.ts +++ b/app/src/utils/is-allowed.ts @@ -7,7 +7,7 @@ export function isAllowed( action: Permission['action'], value: Record | null, strict = false -) { +): boolean { const permissionsStore = usePermissionsStore(); const userStore = useUserStore(); diff --git a/app/src/utils/jwt-payload/jwt-payload.ts b/app/src/utils/jwt-payload/jwt-payload.ts index c98f3c7cff..ed9a8b12fd 100644 --- a/app/src/utils/jwt-payload/jwt-payload.ts +++ b/app/src/utils/jwt-payload/jwt-payload.ts @@ -1,6 +1,6 @@ import { decode } from 'base-64'; -export default function jwtPayload(token: string) { +export default function jwtPayload(token: string): any { const payloadBase64 = token.split('.')[1].replace('-', '+').replace('_', '/'); const payloadDecoded = decode(payloadBase64); const payloadObject = JSON.parse(payloadDecoded); diff --git a/app/src/utils/move-in-array/move-in-array.ts b/app/src/utils/move-in-array/move-in-array.ts index 32012e0f2b..8ed2e67bad 100644 --- a/app/src/utils/move-in-array/move-in-array.ts +++ b/app/src/utils/move-in-array/move-in-array.ts @@ -1,4 +1,4 @@ -export default function moveInArray(array: readonly any[], fromIndex: number, toIndex: number) { +export default function moveInArray(array: readonly any[], fromIndex: number, toIndex: number): readonly any[] | any[] { const item = array[fromIndex]; const length = array.length; const diff = fromIndex - toIndex; diff --git a/app/src/utils/notify.ts b/app/src/utils/notify.ts index 0405d56cee..0bb961fe91 100644 --- a/app/src/utils/notify.ts +++ b/app/src/utils/notify.ts @@ -3,7 +3,7 @@ import { NotificationRaw } from '@/types'; let store: any; -export function notify(notification: NotificationRaw) { +export function notify(notification: NotificationRaw): void { if (!store) store = useNotificationsStore(); store.add(notification); } diff --git a/app/src/utils/parse-filter.ts b/app/src/utils/parse-filter.ts index fcbb8eea5f..bbeb421868 100644 --- a/app/src/utils/parse-filter.ts +++ b/app/src/utils/parse-filter.ts @@ -1,7 +1,7 @@ import { deepMap } from './deep-map'; import { useUserStore } from '@/stores'; -export function parseFilter(filter: Record) { +export function parseFilter(filter: Record): Record { const userStore = useUserStore(); return deepMap(filter, (val: any, key: string) => { diff --git a/app/src/utils/readable-mime-type/readable-mime-type.ts b/app/src/utils/readable-mime-type/readable-mime-type.ts index 1f4fb2718e..cc7445a832 100644 --- a/app/src/utils/readable-mime-type/readable-mime-type.ts +++ b/app/src/utils/readable-mime-type/readable-mime-type.ts @@ -1,7 +1,7 @@ import types from './types.json'; import extensions from './extensions.json'; -export default function readableMimeType(type: string, extension = false) { +export default function readableMimeType(type: string, extension = false): string | null { if (extension) { return (extensions as any)[type] || null; } diff --git a/app/src/utils/register-component/register-component.ts b/app/src/utils/register-component/register-component.ts index 4e0910bef8..6a3bfaa193 100644 --- a/app/src/utils/register-component/register-component.ts +++ b/app/src/utils/register-component/register-component.ts @@ -3,7 +3,8 @@ import Vue, { Component, AsyncComponent } from 'vue'; function registerComponent(id: string, component: Component | AsyncComponent): void; function registerComponent(id: string, component: Parameters[1]): void; -function registerComponent(id: string, component: any) { +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +function registerComponent(id: string, component: any): void { Vue.component(id, component); } diff --git a/app/src/utils/render-string-template.ts b/app/src/utils/render-string-template.ts index 17dbf958e3..e9d53a484f 100644 --- a/app/src/utils/render-string-template.ts +++ b/app/src/utils/render-string-template.ts @@ -1,21 +1,28 @@ -import { computed, Ref } from '@vue/composition-api'; +import { computed, ComputedRef, Ref } from '@vue/composition-api'; import { render } from 'micromustache'; import { getFieldsFromTemplate } from './get-fields-from-template'; +type StringTemplate = { + fieldsInTemplate: ComputedRef; + displayValue: ComputedRef; +}; + export function renderStringTemplate( template: Ref | string, item: Ref | undefined | null> -) { +): StringTemplate { const templateString = computed(() => (typeof template === 'string' ? template : template.value)); const fieldsInTemplate = computed(() => getFieldsFromTemplate(templateString.value)); const displayValue = computed(() => { - if (!item.value || !templateString.value || !fieldsInTemplate.value) return; + if (!item.value || !templateString.value || !fieldsInTemplate.value) return false; try { return render(templateString.value, item.value, { propsExist: true }); - } catch {} + } catch { + return false; + } }); return { fieldsInTemplate, displayValue }; diff --git a/app/src/utils/set-favicon/set-favicon.ts b/app/src/utils/set-favicon/set-favicon.ts index 2823e4430c..a4df756f41 100644 --- a/app/src/utils/set-favicon/set-favicon.ts +++ b/app/src/utils/set-favicon/set-favicon.ts @@ -15,19 +15,20 @@ const svg = (color: string, hide: boolean) => ` } `; -export default function setFavicon(color = '#00C897', hide = false) { +export default function setFavicon(color = '#00C897', hide = false): void { const icon = svg(color, hide); const wrapper = document.createElement('div'); wrapper.innerHTML = icon.trim(); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const iconSerialized = new XMLSerializer().serializeToString(wrapper.firstChild!); + if (wrapper.firstChild) { + const iconSerialized = new XMLSerializer().serializeToString(wrapper.firstChild); - const string = 'data:image/svg+xml;base64,' + window.btoa(iconSerialized); + const string = 'data:image/svg+xml;base64,' + window.btoa(iconSerialized); - const link: HTMLLinkElement = document.querySelector("link[rel*='icon']") || document.createElement('link'); - link.type = 'image/x-icon'; - link.rel = 'icon'; - link.href = string; - document.getElementsByTagName('head')[0].appendChild(link); + const link: HTMLLinkElement = document.querySelector("link[rel*='icon']") || document.createElement('link'); + link.type = 'image/x-icon'; + link.rel = 'icon'; + link.href = string; + document.getElementsByTagName('head')[0].appendChild(link); + } } diff --git a/app/src/utils/translate-object-values.ts b/app/src/utils/translate-object-values.ts index f35f5a2d18..76df0c1cb0 100644 --- a/app/src/utils/translate-object-values.ts +++ b/app/src/utils/translate-object-values.ts @@ -1,7 +1,7 @@ import i18n from '@/lang'; import { cloneDeep } from 'lodash'; -export function translate>(obj: T) { +export function translate>(obj: T): any { obj = cloneDeep(obj); Object.entries(obj).forEach(([key, val]) => { diff --git a/app/src/utils/translate-shortcut/translate-shortcut.ts b/app/src/utils/translate-shortcut/translate-shortcut.ts index 6481738a80..a6bb36bd99 100644 --- a/app/src/utils/translate-shortcut/translate-shortcut.ts +++ b/app/src/utils/translate-shortcut/translate-shortcut.ts @@ -1,6 +1,6 @@ import capitalizeFirst from '@/utils/capitalize-first'; -export default function translateShortcut(keys: string[]) { +export default function translateShortcut(keys: string[]): string { const isMac = navigator.platform.toLowerCase().startsWith('mac') || navigator.platform.startsWith('iP'); if (isMac) { diff --git a/app/src/utils/unexpected-error.ts b/app/src/utils/unexpected-error.ts index 816246cbdb..0f3fa7759b 100644 --- a/app/src/utils/unexpected-error.ts +++ b/app/src/utils/unexpected-error.ts @@ -5,7 +5,7 @@ import { APIError } from '@/types'; let store: any; -export function unexpectedError(error: Error | RequestError | APIError) { +export function unexpectedError(error: Error | RequestError | APIError): void { if (!store) store = useNotificationsStore(); const code = diff --git a/app/src/utils/upload-file/upload-file.ts b/app/src/utils/upload-file/upload-file.ts index 584a2fa1cc..842d8e2df7 100644 --- a/app/src/utils/upload-file/upload-file.ts +++ b/app/src/utils/upload-file/upload-file.ts @@ -13,7 +13,7 @@ export default async function uploadFile( preset?: Record; fileId?: string; } -) { +): Promise { const progressHandler = options?.onProgressChange || (() => undefined); const formData = new FormData(); diff --git a/app/src/utils/upload-files/upload-files.ts b/app/src/utils/upload-files/upload-files.ts index d426b7e59d..7462b10cb9 100644 --- a/app/src/utils/upload-files/upload-files.ts +++ b/app/src/utils/upload-files/upload-files.ts @@ -10,7 +10,7 @@ export default async function uploadFiles( notifications?: boolean; preset?: Record; } -) { +): Promise { const progressHandler = options?.onProgressChange || (() => undefined); const progressForFiles = files.map(() => 0); diff --git a/app/src/views/private/components/drawer-item/drawer-item.vue b/app/src/views/private/components/drawer-item/drawer-item.vue index 244e1ea93c..074e318887 100644 --- a/app/src/views/private/components/drawer-item/drawer-item.vue +++ b/app/src/views/private/components/drawer-item/drawer-item.vue @@ -157,7 +157,7 @@ export default defineComponent({ const fields = computed(() => { if (props.circularField) { - return fieldsWithPermissions.value.filter((field) => { + return fieldsWithPermissions.value.filter((field: Field) => { return field.field !== props.circularField; }); } else { diff --git a/app/src/views/private/components/filter-sidebar-detail/get-available-operators-for-type.ts b/app/src/views/private/components/filter-sidebar-detail/get-available-operators-for-type.ts index 7308075783..58749bd90d 100644 --- a/app/src/views/private/components/filter-sidebar-detail/get-available-operators-for-type.ts +++ b/app/src/views/private/components/filter-sidebar-detail/get-available-operators-for-type.ts @@ -1,4 +1,6 @@ -export default function getAvailableOperatorsForType(type: string) { +import { OperatorType } from './types'; + +export default function getAvailableOperatorsForType(type: string): OperatorType { /** * @NOTE * In the filter, you can't filter on the relational field itself, so we don't have to account @@ -15,7 +17,6 @@ export default function getAvailableOperatorsForType(type: string) { case 'lang': case 'uuid': case 'hash': - case 'array': case 'string': return { type: 'text', diff --git a/app/src/views/private/components/filter-sidebar-detail/types.ts b/app/src/views/private/components/filter-sidebar-detail/types.ts index 017198e912..cff374dd39 100644 --- a/app/src/views/private/components/filter-sidebar-detail/types.ts +++ b/app/src/views/private/components/filter-sidebar-detail/types.ts @@ -5,3 +5,8 @@ export type FieldTree = { name: string | TranslateResult; children?: FieldTree[]; }; + +export type OperatorType = { + type: string; + operators: string[]; +}; diff --git a/app/src/views/private/components/image-editor/image-editor.vue b/app/src/views/private/components/image-editor/image-editor.vue index c573c1c706..ceed647b1d 100644 --- a/app/src/views/private/components/image-editor/image-editor.vue +++ b/app/src/views/private/components/image-editor/image-editor.vue @@ -312,7 +312,6 @@ export default defineComponent({ return 'crop_square'; case imageData.value.width / imageData.value.height: return 'crop_original'; - case NaN: default: return 'crop_free'; } diff --git a/app/src/views/private/components/latency-indicator/latency-indicator.vue b/app/src/views/private/components/latency-indicator/latency-indicator.vue index 210660f62f..96cdd4caab 100644 --- a/app/src/views/private/components/latency-indicator/latency-indicator.vue +++ b/app/src/views/private/components/latency-indicator/latency-indicator.vue @@ -51,10 +51,12 @@ export default defineComponent({ return `${i18n.t('connection_fair')}\n(${ms(avgLatency.value)} ${i18n.t('latency')})`; case 1: return `${i18n.t('connection_poor')}\n(${ms(avgLatency.value)} ${i18n.t('latency')})`; + default: + return null; } }); - const icon = computed(() => { + const icon = computed(() => { switch (connectionStrength.value) { case 4: return 'signal_wifi_4_bar'; @@ -64,6 +66,8 @@ export default defineComponent({ return 'signal_wifi_2_bar'; case 1: return 'signal_wifi_1_bar'; + default: + return null; } }); diff --git a/app/src/views/private/components/module-bar/module-bar.vue b/app/src/views/private/components/module-bar/module-bar.vue index 8f06d5e75d..eecff0091c 100644 --- a/app/src/views/private/components/module-bar/module-bar.vue +++ b/app/src/views/private/components/module-bar/module-bar.vue @@ -34,6 +34,7 @@ import ModuleBarLogo from '../module-bar-logo/'; import ModuleBarAvatar from '../module-bar-avatar/'; import { useUserStore } from '@/stores/'; import { orderBy } from 'lodash'; +import { ModuleConfig } from '@/modules/types'; export default defineComponent({ components: { @@ -49,12 +50,12 @@ export default defineComponent({ const registeredModules = orderBy( modules.value - .map((module) => ({ + .map((module: ModuleConfig) => ({ ...module, href: module.link || null, to: module.link === undefined ? `/${module.id}/` : null, })) - .filter((module) => { + .filter((module: ModuleConfig) => { if (module.hidden !== undefined) { if ((module.hidden as boolean) === true || (module.hidden as Ref).value === true) { return false; diff --git a/app/src/views/private/components/render-display/render-display.vue b/app/src/views/private/components/render-display/render-display.vue index ac01a487cf..65e3b7b878 100644 --- a/app/src/views/private/components/render-display/render-display.vue +++ b/app/src/views/private/components/render-display/render-display.vue @@ -23,6 +23,7 @@ import { defineComponent, computed } from '@vue/composition-api'; import { getDisplays } from '@/displays'; import ValueNull from '@/views/private/components/value-null'; +import { DisplayConfig } from '@/displays/types'; export default defineComponent({ components: { ValueNull }, @@ -62,7 +63,9 @@ export default defineComponent({ }, setup(props) { const { displays } = getDisplays(); - const displayInfo = computed(() => displays.value.find((display) => display.id === props.display) || null); + const displayInfo = computed( + () => displays.value.find((display: DisplayConfig) => display.id === props.display) || null + ); return { displayInfo }; }, }); diff --git a/app/src/views/private/components/render-template/render-template.vue b/app/src/views/private/components/render-template/render-template.vue index 4cd4ee42aa..667d1972d0 100644 --- a/app/src/views/private/components/render-template/render-template.vue +++ b/app/src/views/private/components/render-template/render-template.vue @@ -27,6 +27,7 @@ import { get } from 'lodash'; import { Field } from '@/types'; import { getDisplays } from '@/displays'; import ValueNull from '@/views/private/components/value-null'; +import { DisplayConfig, DisplayHandlerFunction } from '@/displays/types'; export default defineComponent({ components: { ValueNull }, @@ -89,14 +90,14 @@ export default defineComponent({ // If no display is configured, we can render the raw value if (!field || !field.meta?.display) return value; - const displayInfo = displays.value.find((display) => display.id === field.meta?.display); + const displayInfo = displays.value.find((display: DisplayConfig) => display.id === field.meta?.display); // If used display doesn't exist in the current project, return raw value if (!displayInfo) return value; // If the display handler is a function, we parse the value and return the result if (typeof displayInfo.handler === 'function') { - const handler = displayInfo.handler as Function; + const handler = displayInfo.handler as DisplayHandlerFunction; return handler(value, field.meta?.display_options); } diff --git a/app/src/views/private/components/user-popover/user-popover.vue b/app/src/views/private/components/user-popover/user-popover.vue index a4c73f908f..d1058ada74 100644 --- a/app/src/views/private/components/user-popover/user-popover.vue +++ b/app/src/views/private/components/user-popover/user-popover.vue @@ -63,6 +63,7 @@ export default defineComponent({ if (data.value.avatar?.id) { return addTokenToURL(`${getRootPath()}assets/${data.value.avatar.id}?key=system-medium-cover`); } + return null; }); const active = ref(false); diff --git a/app/vue.config.js b/app/vue.config.js index c6ba06a0f7..ea21846750 100644 --- a/app/vue.config.js +++ b/app/vue.config.js @@ -1,6 +1,3 @@ -/* @tslint:disable */ -/* eslint-disable */ - const WebpackAssetsManifest = require('webpack-assets-manifest'); module.exports = { diff --git a/packages/drive-azure/src/AzureBlobWebServices.ts b/packages/drive-azure/src/AzureBlobWebServices.ts index c2151c2a36..2a5e7798da 100644 --- a/packages/drive-azure/src/AzureBlobWebServices.ts +++ b/packages/drive-azure/src/AzureBlobWebServices.ts @@ -53,7 +53,7 @@ export class AzureBlobWebServicesStorage extends Storage { /** * Prefixes the given filePath with the storage root location */ - protected _fullPath(filePath: string) { + protected _fullPath(filePath: string): string { return normalize(path.join(this.$root, filePath)); } diff --git a/packages/drive-gcs/src/GoogleCloudStorage.ts b/packages/drive-gcs/src/GoogleCloudStorage.ts index 7945937c68..0b64d22a6a 100644 --- a/packages/drive-gcs/src/GoogleCloudStorage.ts +++ b/packages/drive-gcs/src/GoogleCloudStorage.ts @@ -63,7 +63,7 @@ export class GoogleCloudStorage extends Storage { /** * Prefixes the given filePath with the storage root location */ - protected _fullPath(filePath: string) { + protected _fullPath(filePath: string): string { return normalize(path.join(this.$root, filePath)); } @@ -94,13 +94,13 @@ export class GoogleCloudStorage extends Storage { const result = await this._file(location).delete(); return { raw: result, wasDeleted: true }; } catch (e) { - e = handleError(e, location); + const error = handleError(e, location); - if (e instanceof FileNotFound) { + if (error instanceof FileNotFound) { return { raw: undefined, wasDeleted: false }; } - throw e; + throw error; } } diff --git a/packages/drive-s3/src/AmazonWebServicesS3Storage.ts b/packages/drive-s3/src/AmazonWebServicesS3Storage.ts index 9f68c98b03..328fef4454 100644 --- a/packages/drive-s3/src/AmazonWebServicesS3Storage.ts +++ b/packages/drive-s3/src/AmazonWebServicesS3Storage.ts @@ -39,7 +39,6 @@ export class AmazonWebServicesS3Storage extends Storage { constructor(config: AmazonWebServicesS3StorageConfig) { super(); - // eslint-disable-next-line @typescript-eslint/no-var-requires const S3 = require('aws-sdk/clients/s3'); this.$driver = new S3({ @@ -56,7 +55,7 @@ export class AmazonWebServicesS3Storage extends Storage { /** * Prefixes the given filePath with the storage root location */ - protected _fullPath(filePath: string) { + protected _fullPath(filePath: string): string { return normalize(path.join(this.$root, filePath)); } diff --git a/packages/drive/src/LocalFileSystemStorage.ts b/packages/drive/src/LocalFileSystemStorage.ts index f732f7cbea..61683e28fc 100644 --- a/packages/drive/src/LocalFileSystemStorage.ts +++ b/packages/drive/src/LocalFileSystemStorage.ts @@ -72,13 +72,13 @@ export class LocalFileSystemStorage extends Storage { const result = await fse.unlink(this._fullPath(location)); return { raw: result, wasDeleted: true }; } catch (e) { - e = handleError(e, location); + const error = handleError(e, location); - if (e instanceof FileNotFound) { + if (error instanceof FileNotFound) { return { raw: undefined, wasDeleted: false }; } - throw e; + throw error; } } diff --git a/packages/drive/src/utils.ts b/packages/drive/src/utils.ts index b1a672544d..daca4e524d 100644 --- a/packages/drive/src/utils.ts +++ b/packages/drive/src/utils.ts @@ -6,7 +6,7 @@ import Storage from './Storage'; * Returns a boolean indication if stream param * is a readable stream or not. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function isReadableStream(stream: any): stream is NodeJS.ReadableStream { return ( stream !== null && diff --git a/packages/drive/tests/LocalFileSystemStorage.test.ts b/packages/drive/tests/LocalFileSystemStorage.test.ts index 565b182b1d..7a4afac38b 100644 --- a/packages/drive/tests/LocalFileSystemStorage.test.ts +++ b/packages/drive/tests/LocalFileSystemStorage.test.ts @@ -39,8 +39,6 @@ jest.mock('fs-extra', () => { errors.map = map; try { await handler(); - } catch (e) { - throw e; } finally { errors.map = {}; } diff --git a/packages/drive/tests/StorageManager.test.ts b/packages/drive/tests/StorageManager.test.ts index 3d01c96d74..872c7e1327 100644 --- a/packages/drive/tests/StorageManager.test.ts +++ b/packages/drive/tests/StorageManager.test.ts @@ -29,7 +29,7 @@ describe('Storage Manager', () => { const storageManager = new StorageManager({ default: 'local', disks: { - // @ts-expect-error + // @ts-expect-error No driver local: {}, }, }); diff --git a/packages/schema/src/dialects/mysql.ts b/packages/schema/src/dialects/mysql.ts index fe033ebbc3..454184f18b 100644 --- a/packages/schema/src/dialects/mysql.ts +++ b/packages/schema/src/dialects/mysql.ts @@ -3,7 +3,7 @@ import { SchemaOverview } from '../types/overview'; import { SchemaInspector } from '../types/schema'; export default class MySQL extends KnexMySQL implements SchemaInspector { - async overview() { + async overview(): Promise { const columns = await this.knex.raw( ` SELECT diff --git a/packages/schema/src/dialects/oracledb.ts b/packages/schema/src/dialects/oracledb.ts index 511783a09f..8e57fce3d0 100644 --- a/packages/schema/src/dialects/oracledb.ts +++ b/packages/schema/src/dialects/oracledb.ts @@ -4,7 +4,7 @@ import { SchemaInspector } from '../types/schema'; import { mapKeys } from 'lodash'; export default class Oracle extends KnexOracle implements SchemaInspector { - async overview() { + async overview(): Promise { type RawColumn = { TABLE_NAME: string; COLUMN_NAME: string; diff --git a/packages/schema/src/dialects/postgres.ts b/packages/schema/src/dialects/postgres.ts index 6b36cbb2c6..3efcb51c23 100644 --- a/packages/schema/src/dialects/postgres.ts +++ b/packages/schema/src/dialects/postgres.ts @@ -3,7 +3,7 @@ import { SchemaOverview } from '../types/overview'; import { SchemaInspector } from '../types/schema'; export default class Postgres extends KnexPostgres implements SchemaInspector { - async overview() { + async overview(): Promise { const [columnsResult, primaryKeysResult] = await Promise.all([ // Only select columns from BASE TABLEs to exclude views (Postgres views // cannot have primary keys so they cannot be used) diff --git a/packages/schema/src/dialects/sqlite.ts b/packages/schema/src/dialects/sqlite.ts index b802926549..5f833f7732 100644 --- a/packages/schema/src/dialects/sqlite.ts +++ b/packages/schema/src/dialects/sqlite.ts @@ -12,7 +12,7 @@ type RawColumn = { }; export default class SQLite extends KnexSQLite implements SchemaInspector { - async overview() { + async overview(): Promise { const tablesWithAutoIncrementPrimaryKeys = ( await this.knex.select('name').from('sqlite_master').whereRaw(`sql LIKE "%AUTOINCREMENT%"`) ).map(({ name }) => name); diff --git a/packages/sdk/src/base/auth.ts b/packages/sdk/src/base/auth.ts index 6a68d99523..b4172455e7 100644 --- a/packages/sdk/src/base/auth.ts +++ b/packages/sdk/src/base/auth.ts @@ -33,7 +33,9 @@ export class Auth implements IAuth { this.refresher = new Debouncer(this.refreshToken.bind(this)); try { this.updateRefresh(this.options?.refresh); - } catch (err) {} + } catch { + // Ignore error + } } get token(): string | null { @@ -122,8 +124,12 @@ export class Auth implements IAuth { if (this.options.refresh!.auto) { this.timer = setTimeout(() => { this.refresh() - .then(() => {}) - .catch((_) => {}); + .then(() => { + // Do nothing + }) + .catch(() => { + // Do nothing + }); }, remaining); } } diff --git a/packages/sdk/src/handlers/activity.ts b/packages/sdk/src/handlers/activity.ts index 51cce669d4..32f7e0f5a6 100644 --- a/packages/sdk/src/handlers/activity.ts +++ b/packages/sdk/src/handlers/activity.ts @@ -17,7 +17,7 @@ export class ActivityHandler extends ItemsHandler { return this._comments; } } diff --git a/packages/sdk/src/handlers/extensions.ts b/packages/sdk/src/handlers/extensions.ts index 59a95dcae8..4b697b0348 100644 --- a/packages/sdk/src/handlers/extensions.ts +++ b/packages/sdk/src/handlers/extensions.ts @@ -64,7 +64,7 @@ export class ExtensionHandler { this.transport = transport; } - endpoint(name: string) { + endpoint(name: string): ExtensionEndpoint { return new ExtensionEndpoint(this.transport, name); } } diff --git a/packages/sdk/src/handlers/graphql.ts b/packages/sdk/src/handlers/graphql.ts index 45879d3e9a..96033b7596 100644 --- a/packages/sdk/src/handlers/graphql.ts +++ b/packages/sdk/src/handlers/graphql.ts @@ -14,10 +14,12 @@ export class GraphQLHandler { }); } + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async items(query: string, variables?: any): Promise> { return await this.request('/graphql', query, variables); } + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async system(query: string, variables?: any): Promise> { return await this.request('/graphql/system', query, variables); } diff --git a/packages/sdk/tests/base/storage/tests.ts b/packages/sdk/tests/base/storage/tests.ts index 5a739ec8d6..416d65fd36 100644 --- a/packages/sdk/tests/base/storage/tests.ts +++ b/packages/sdk/tests/base/storage/tests.ts @@ -1,7 +1,7 @@ import { IStorage } from '../../../src/storage'; export function createStorageTests(createStorage: () => IStorage) { - return function () { + return function (): void { beforeEach(() => { // These run both in node and browser mode if (typeof localStorage !== 'undefined') { diff --git a/packages/sdk/tests/base/transport/axios.test.ts b/packages/sdk/tests/base/transport/axios.test.ts index 9f58cf5afb..d60a8a1acf 100644 --- a/packages/sdk/tests/base/transport/axios.test.ts +++ b/packages/sdk/tests/base/transport/axios.test.ts @@ -77,11 +77,11 @@ describe('axios transport', function () { } catch (err) { const terr = err as TransportError; expect(terr).toBeInstanceOf(TransportError); - expect(terr.response!.status).toBe(403); + expect(terr.response?.status).toBe(403); expect(terr.message).toBe('You don\'t have permission access to "contacts" collection.'); expect(terr.errors.length).toBe(1); - expect(terr.errors[0]!.message).toBe('You don\'t have permission access to "contacts" collection.'); - expect(terr.errors[0]!.extensions?.code).toBe('FORBIDDEN'); + expect(terr.errors[0]?.message).toBe('You don\'t have permission access to "contacts" collection.'); + expect(terr.errors[0]?.extensions?.code).toBe('FORBIDDEN'); } }); @@ -101,7 +101,7 @@ describe('axios transport', function () { expect(terr.response).toBeUndefined(); expect(terr.message).toBe('Random error'); expect(terr.parent).not.toBeUndefined(); - expect(terr.parent!.message).toBe('Random error'); + expect(terr.parent?.message).toBe('Random error'); } }); }); @@ -129,7 +129,7 @@ describe('axios transport', function () { expect(terr.response).toBeUndefined(); expect(terr.message).toBe('this is not an axios error'); expect(terr.parent).not.toBeUndefined(); - expect(terr.parent!.message).toBe('this is not an axios error'); + expect(terr.parent?.message).toBe('this is not an axios error'); } }); diff --git a/packages/sdk/tests/handlers/utils.test.ts b/packages/sdk/tests/handlers/utils.test.ts index 280a82cb06..aae524c2b8 100644 --- a/packages/sdk/tests/handlers/utils.test.ts +++ b/packages/sdk/tests/handlers/utils.test.ts @@ -37,7 +37,7 @@ describe('utils', function () { const sdk = new Directus(url); const hash = await sdk.utils.hash.generate('wolfulus'); - expect(hash.substr(0, 7)).toBe('$argon2'); + expect(hash?.substr(0, 7)).toBe('$argon2'); }); test(`hash verify`, async (url, nock) => { @@ -54,7 +54,7 @@ describe('utils', function () { const sdk = new Directus(url); const hash = await sdk.utils.hash.generate('wolfulus'); - expect(hash.substr(0, 7)).toBe('$argon2'); + expect(hash?.substr(0, 7)).toBe('$argon2'); nock() .post('/utils/hash/verify') @@ -64,7 +64,7 @@ describe('utils', function () { }; }); - const result = await sdk.utils.hash.verify('wolfulus', hash); + const result = await sdk.utils.hash.verify('wolfulus', hash || ''); expect(result).toBe(true); }); diff --git a/packages/sdk/tests/items.test.ts b/packages/sdk/tests/items.test.ts index b6a2c2f6a0..0e0c15c58f 100644 --- a/packages/sdk/tests/items.test.ts +++ b/packages/sdk/tests/items.test.ts @@ -23,9 +23,9 @@ describe('items', function () { expect(item).not.toBeNull(); expect(item).not.toBeUndefined(); - expect(item!.id).toBe(1); - expect(item!.title).toBe(`My first post`); - expect(item!.body).toBe('

Hey there!

'); + expect(item?.id).toBe(1); + expect(item?.title).toBe(`My first post`); + expect(item?.body).toBe('

Hey there!

'); }); test(`should encode ids`, async (url, nock) => { @@ -43,8 +43,8 @@ describe('items', function () { expect(item).not.toBeNull(); expect(item).not.toBeUndefined(); - expect(item!.slug).toBe('double slash'); - expect(item!.name).toBe('Double Slash'); + expect(item?.slug).toBe('double slash'); + expect(item?.name).toBe('Double Slash'); }); test(`can get multiple items by id`, async (url, nock) => { @@ -70,14 +70,14 @@ describe('items', function () { const sdk = new Directus(url); const items = await sdk.items('posts').readMany(); - expect(items.data![0]).toMatchObject({ + expect(items.data?.[0]).toMatchObject({ id: 1, title: 'My first post', body: '

Hey there!

', published: false, }); - expect(items.data![1]).toMatchObject({ + expect(items.data?.[1]).toMatchObject({ id: 2, title: 'My second post', body: '

Hey there!

', @@ -109,13 +109,13 @@ describe('items', function () { fields: ['id', 'title'], }); - expect(items.data![0]!.id).toBe(1); - expect(items.data![0]!.title).toBe(`My first post`); - expect(items.data![0]!.body).toBeUndefined(); + expect(items.data?.[0]?.id).toBe(1); + expect(items.data?.[0]?.title).toBe(`My first post`); + expect(items.data?.[0]?.body).toBeUndefined(); - expect(items.data![1]!.id).toBe(2); - expect(items.data![1]!.title).toBe(`My second post`); - expect(items.data![1]!.body).toBeUndefined(); + expect(items.data?.[1]?.id).toBe(2); + expect(items.data?.[1]?.title).toBe(`My second post`); + expect(items.data?.[1]?.body).toBeUndefined(); }); test(`create one item`, async (url, nock) => { @@ -179,14 +179,14 @@ describe('items', function () { }, ]); - expect(items.data![0]).toMatchObject({ + expect(items.data?.[0]).toMatchObject({ id: 4, title: 'New post 2', body: 'This is a new post 2', published: false, }); - expect(items.data![1]).toMatchObject({ + expect(items.data?.[1]).toMatchObject({ id: 5, title: 'New post 3', body: 'This is a new post 3', @@ -256,14 +256,14 @@ describe('items', function () { }, ]); - expect(item.data![0]).toMatchObject({ + expect(item.data?.[0]).toMatchObject({ id: 1, title: 'Updated post', body: 'Updated post content', published: true, }); - expect(item.data![1]).toMatchObject({ + expect(item.data?.[1]).toMatchObject({ id: 2, title: 'Updated post 2', body: 'Updated post content 2', diff --git a/packages/sdk/tests/singleton.test.ts b/packages/sdk/tests/singleton.test.ts index c99d0152e1..adc5d061bd 100644 --- a/packages/sdk/tests/singleton.test.ts +++ b/packages/sdk/tests/singleton.test.ts @@ -36,9 +36,9 @@ describe('singleton', function () { expect(settings).not.toBeNull(); expect(settings).not.toBeUndefined(); - expect(settings!.url).toBe('http://website.com'); - expect(settings!.title).toBe(`Website Title`); - expect(settings!.show_menu).toBe(true); + expect(settings?.url).toBe('http://website.com'); + expect(settings?.title).toBe(`Website Title`); + expect(settings?.show_menu).toBe(true); }); test(`can update an item`, async (url, nock) => { @@ -63,8 +63,8 @@ describe('singleton', function () { expect(settings).not.toBeNull(); expect(settings).not.toBeUndefined(); - expect(settings!.url).toBe('http://website.com'); - expect(settings!.title).toBe(`New Website Title`); - expect(settings!.show_menu).toBe(true); + expect(settings?.url).toBe('http://website.com'); + expect(settings?.title).toBe(`New Website Title`); + expect(settings?.show_menu).toBe(true); }); }); diff --git a/packages/sdk/tests/utils.ts b/packages/sdk/tests/utils.ts index 0d86640fd1..d396912824 100644 --- a/packages/sdk/tests/utils.ts +++ b/packages/sdk/tests/utils.ts @@ -14,7 +14,7 @@ export type TestSettings = { fixture?: string; }; -export function test(name: string, test: Test, settings?: TestSettings) { +export function test(name: string, test: Test, settings?: TestSettings): void { it(name, async () => { nock.cleanAll(); @@ -40,7 +40,7 @@ export async function timers( skip: (func: () => Promise, date?: boolean) => Promise; }) => Promise, initial: number = Date.now() -) { +): Promise { const originals = { setTimeout: global.setTimeout, setImmediate: global.setImmediate, diff --git a/tests/setup/global.ts b/tests/setup/global.ts new file mode 100644 index 0000000000..c4080308c0 --- /dev/null +++ b/tests/setup/global.ts @@ -0,0 +1,16 @@ +import Dockerode from 'dockerode'; +import { Knex } from 'knex'; + +type Global = { + databaseContainers: { vendor: string; container: Dockerode.Container }[]; + directusContainers: { vendor: string; container: Dockerode.Container }[]; + knexInstances: { vendor: string; knex: Knex }[]; +}; + +const global: Global = { + databaseContainers: [], + directusContainers: [], + knexInstances: [], +}; + +export default global; diff --git a/tests/setup/setup.ts b/tests/setup/setup.ts index 897ca6ee86..a46d60c52a 100644 --- a/tests/setup/setup.ts +++ b/tests/setup/setup.ts @@ -1,5 +1,5 @@ import Dockerode, { ContainerSpec } from 'dockerode'; -import knex, { Knex } from 'knex'; +import knex from 'knex'; import { awaitDatabaseConnection, awaitDirectusConnection } from './utils/await-connection'; import Listr, { ListrTask } from 'listr'; import { getDBsToTest } from '../get-dbs-to-test'; @@ -8,17 +8,12 @@ import globby from 'globby'; import path from 'path'; import { GlobalConfigTsJest } from 'ts-jest/dist/types'; import { writeFileSync } from 'fs'; - -declare module global { - let databaseContainers: { vendor: string; container: Dockerode.Container }[]; - let directusContainers: { vendor: string; container: Dockerode.Container }[]; - let knexInstances: { vendor: string; knex: Knex }[]; -} +import global from './global'; const docker = new Dockerode(); let started = false; -export default async (jestConfig: GlobalConfigTsJest) => { +export default async (jestConfig: GlobalConfigTsJest): Promise => { if (started) return; started = true; @@ -26,10 +21,6 @@ export default async (jestConfig: GlobalConfigTsJest) => { console.log(`👮‍♀️ Starting tests!\n`); - global.databaseContainers = []; - global.directusContainers = []; - global.knexInstances = []; - const vendors = getDBsToTest(); const NODE_VERSION = process.env.TEST_NODE_VERSION || '15-alpine'; diff --git a/tests/setup/teardown.ts b/tests/setup/teardown.ts index 3876ebabce..e9a940a5e3 100644 --- a/tests/setup/teardown.ts +++ b/tests/setup/teardown.ts @@ -1,23 +1,18 @@ import Listr from 'listr'; -import { Knex } from 'knex'; import Dockerode from 'dockerode'; import { getDBsToTest } from '../get-dbs-to-test'; import config, { CONTAINER_PERSISTENCE_FILE } from '../config'; import { GlobalConfigTsJest } from 'ts-jest/dist/types'; import { readFileSync, rmSync } from 'fs'; +import global from './global'; -declare module global { - let databaseContainers: { vendor: string; container: Dockerode.Container }[]; - let directusContainers: { vendor: string; container: Dockerode.Container }[]; - let knexInstances: { vendor: string; knex: Knex }[]; -} const docker = new Dockerode(); if (require.main === module) { teardown(undefined, true); } -export default async function teardown(jestConfig?: GlobalConfigTsJest, isAfterWatch = false) { +export default async function teardown(jestConfig?: GlobalConfigTsJest, isAfterWatch = false): Promise { if (jestConfig?.watch || jestConfig?.watchAll) return; const vendors = getDBsToTest(); diff --git a/tests/setup/utils/await-connection.ts b/tests/setup/utils/await-connection.ts index 7de7b7f925..f465a053a1 100644 --- a/tests/setup/utils/await-connection.ts +++ b/tests/setup/utils/await-connection.ts @@ -1,7 +1,11 @@ import { Knex } from 'knex'; import axios from 'axios'; -export async function awaitDatabaseConnection(database: Knex, checkSQL: string, currentAttempt: number = 0) { +export async function awaitDatabaseConnection( + database: Knex, + checkSQL: string, + currentAttempt = 0 +): Promise { try { await database.raw(checkSQL); } catch { @@ -18,7 +22,7 @@ export async function awaitDatabaseConnection(database: Knex, checkSQL: string, } } -export async function awaitDirectusConnection(port: number = 6100, currentAttempt: number = 0) { +export async function awaitDirectusConnection(port = 6100, currentAttempt = 0): Promise { try { await axios.get(`http://localhost:${port}/server/ping`); } catch {